Kernel: Implement simple USB HID driver
This should be easily expandable to add HID devices
This commit is contained in:
		
							parent
							
								
									442ea8a692
								
							
						
					
					
						commit
						1efc6a1385
					
				|  | @ -95,6 +95,7 @@ set(KERNEL_SOURCES | |||
| 	kernel/Timer/RTC.cpp | ||||
| 	kernel/Timer/Timer.cpp | ||||
| 	kernel/USB/Device.cpp | ||||
| 	kernel/USB/HID/HIDDriver.cpp | ||||
| 	kernel/USB/USBManager.cpp | ||||
| 	kernel/USB/XHCI/Controller.cpp | ||||
| 	kernel/USB/XHCI/Device.cpp | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <BAN/ByteSpan.h> | ||||
| #include <BAN/NoCopyMove.h> | ||||
| 
 | ||||
| #include <kernel/Memory/DMARegion.h> | ||||
|  | @ -9,6 +10,18 @@ | |||
| namespace Kernel | ||||
| { | ||||
| 
 | ||||
| 	class USBClassDriver | ||||
| 	{ | ||||
| 		BAN_NON_COPYABLE(USBClassDriver); | ||||
| 		BAN_NON_MOVABLE(USBClassDriver); | ||||
| 
 | ||||
| 	public: | ||||
| 		USBClassDriver() = default; | ||||
| 		virtual ~USBClassDriver() = default; | ||||
| 
 | ||||
| 		virtual void handle_input_data(BAN::ConstByteSpan, uint8_t endpoint_id) = 0; | ||||
| 	}; | ||||
| 
 | ||||
| 	class USBDevice | ||||
| 	{ | ||||
| 		BAN_NON_COPYABLE(USBDevice); | ||||
|  | @ -47,11 +60,13 @@ namespace Kernel | |||
| 
 | ||||
| 		const BAN::Vector<ConfigurationDescriptor>& configurations() { return m_descriptor.configurations; } | ||||
| 
 | ||||
| 		virtual BAN::ErrorOr<void> initialize_endpoint(const USBEndpointDescriptor&) = 0; | ||||
| 		virtual BAN::ErrorOr<size_t> send_request(const USBDeviceRequest&, paddr_t buffer) = 0; | ||||
| 
 | ||||
| 		static USB::SpeedClass determine_speed_class(uint64_t bits_per_second); | ||||
| 
 | ||||
| 	protected: | ||||
| 		void handle_input_data(BAN::ConstByteSpan, uint8_t endpoint_id); | ||||
| 		virtual BAN::ErrorOr<void> initialize_control_endpoint() = 0; | ||||
| 
 | ||||
| 	private: | ||||
|  | @ -60,6 +75,9 @@ namespace Kernel | |||
| 	private: | ||||
| 		DeviceDescriptor m_descriptor; | ||||
| 		BAN::UniqPtr<DMARegion> m_dma_buffer; | ||||
| 
 | ||||
| 		// FIXME: support more than one interface from a configuration
 | ||||
| 		BAN::UniqPtr<USBClassDriver> m_class_driver; | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,94 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <kernel/Input/InputDevice.h> | ||||
| #include <kernel/USB/Device.h> | ||||
| 
 | ||||
| namespace Kernel | ||||
| { | ||||
| 
 | ||||
| 	namespace USBHID | ||||
| 	{ | ||||
| 
 | ||||
| 		struct Report | ||||
| 		{ | ||||
| 			enum class Type { Input, Output, Feature }; | ||||
| 
 | ||||
| 			uint16_t usage_page; | ||||
| 			uint16_t usage_id; | ||||
| 			Type type; | ||||
| 
 | ||||
| 			uint32_t report_count; | ||||
| 			uint32_t report_size; | ||||
| 
 | ||||
| 			uint32_t usage_minimum; | ||||
| 			uint32_t usage_maximum; | ||||
| 
 | ||||
| 			int64_t logical_minimum; | ||||
| 			int64_t logical_maximum; | ||||
| 
 | ||||
| 			int64_t physical_minimum; | ||||
| 			int64_t physical_maximum; | ||||
| 
 | ||||
| 			uint8_t flags; | ||||
| 		}; | ||||
| 
 | ||||
| 		struct Collection | ||||
| 		{ | ||||
| 			uint16_t usage_page; | ||||
| 			uint16_t usage_id; | ||||
| 			uint8_t type; | ||||
| 
 | ||||
| 			BAN::Vector<BAN::Variant<Collection, Report>> entries; | ||||
| 		}; | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	class USBHIDDevice : public InputDevice | ||||
| 	{ | ||||
| 		BAN_NON_COPYABLE(USBHIDDevice); | ||||
| 		BAN_NON_MOVABLE(USBHIDDevice); | ||||
| 
 | ||||
| 	public: | ||||
| 		USBHIDDevice(InputDevice::Type type) | ||||
| 			: InputDevice(type) | ||||
| 		{} | ||||
| 		virtual ~USBHIDDevice() = default; | ||||
| 
 | ||||
| 		virtual void start_report() = 0; | ||||
| 		virtual void stop_report() = 0; | ||||
| 
 | ||||
| 		virtual void handle_variable(uint16_t usage_page, uint16_t usage, int64_t state) = 0; | ||||
| 		virtual void handle_array(uint16_t usage_page, uint16_t usage) = 0; | ||||
| 	}; | ||||
| 
 | ||||
| 	class USBHIDDriver final : public USBClassDriver | ||||
| 	{ | ||||
| 		BAN_NON_COPYABLE(USBHIDDriver); | ||||
| 		BAN_NON_MOVABLE(USBHIDDriver); | ||||
| 
 | ||||
| 	public: | ||||
| 		static BAN::ErrorOr<BAN::UniqPtr<USBHIDDriver>> create(USBDevice&, const USBDevice::InterfaceDescriptor&, uint8_t interface_index); | ||||
| 
 | ||||
| 		void handle_input_data(BAN::ConstByteSpan, uint8_t endpoint_id) override; | ||||
| 
 | ||||
| 	private: | ||||
| 		USBHIDDriver(USBDevice&, const USBDevice::InterfaceDescriptor&, uint8_t interface_index); | ||||
| 		~USBHIDDriver(); | ||||
| 
 | ||||
| 		BAN::ErrorOr<void> initialize(); | ||||
| 
 | ||||
| 		void forward_collection_inputs(const USBHID::Collection&, BAN::ConstByteSpan& data, size_t bit_offset); | ||||
| 
 | ||||
| 	private: | ||||
| 		USBDevice& m_device; | ||||
| 		USBDevice::InterfaceDescriptor m_interface; | ||||
| 		const uint8_t m_interface_index; | ||||
| 
 | ||||
| 		uint8_t m_endpoint_id { 0 }; | ||||
| 		USBHID::Collection m_collection; | ||||
| 		BAN::RefPtr<USBHIDDevice> m_hid_device; | ||||
| 
 | ||||
| 		friend class BAN::UniqPtr<USBHIDDriver>; | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
|  | @ -26,11 +26,15 @@ namespace Kernel | |||
| 			Mutex mutex; | ||||
| 			volatile uint32_t transfer_count { 0 }; | ||||
| 			volatile XHCI::TRB completion_trb; | ||||
| 
 | ||||
| 			BAN::UniqPtr<DMARegion> data_region; | ||||
| 			void(XHCIDevice::*callback)(XHCI::TRB); | ||||
| 		}; | ||||
| 
 | ||||
| 	public: | ||||
| 		static BAN::ErrorOr<BAN::UniqPtr<XHCIDevice>> create(XHCIController&, uint32_t port_id, uint32_t slot_id); | ||||
| 
 | ||||
| 		BAN::ErrorOr<void> initialize_endpoint(const USBEndpointDescriptor&) override; | ||||
| 		BAN::ErrorOr<size_t> send_request(const USBDeviceRequest&, paddr_t buffer) override; | ||||
| 
 | ||||
| 		void on_transfer_event(const volatile XHCI::TRB&); | ||||
|  | @ -47,6 +51,8 @@ namespace Kernel | |||
| 		~XHCIDevice(); | ||||
| 		BAN::ErrorOr<void> update_actual_max_packet_size(); | ||||
| 
 | ||||
| 		void on_interrupt_endpoint_event(XHCI::TRB); | ||||
| 
 | ||||
| 		void advance_endpoint_enqueue(Endpoint&, bool chain); | ||||
| 
 | ||||
| 	private: | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| #include <kernel/Memory/DMARegion.h> | ||||
| #include <kernel/USB/Device.h> | ||||
| #include <kernel/USB/HID/HIDDriver.h> | ||||
| 
 | ||||
| #define DEBUG_USB 0 | ||||
| #define USB_DUMP_DESCRIPTORS 0 | ||||
|  | @ -152,7 +153,8 @@ namespace Kernel | |||
| 						dprintln_if(DEBUG_USB, "Found CommunicationAndCDCControl interface"); | ||||
| 						break; | ||||
| 					case USB::InterfaceBaseClass::HID: | ||||
| 						dprintln_if(DEBUG_USB, "Found HID interface"); | ||||
| 						if (auto result = USBHIDDriver::create(*this, interface, j); !result.is_error()) | ||||
| 							m_class_driver = result.release_value(); | ||||
| 						break; | ||||
| 					case USB::InterfaceBaseClass::Physical: | ||||
| 						dprintln_if(DEBUG_USB, "Found Physical interface"); | ||||
|  | @ -215,6 +217,12 @@ namespace Kernel | |||
| 						dprintln_if(DEBUG_USB, "Invalid interface base class {2H}", interface.descriptor.bInterfaceClass); | ||||
| 						break; | ||||
| 				} | ||||
| 
 | ||||
| 				if (m_class_driver) | ||||
| 				{ | ||||
| 					dprintln("Successfully initialized USB interface"); | ||||
| 					return {}; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -299,6 +307,12 @@ namespace Kernel | |||
| 		return BAN::move(configuration); | ||||
| 	} | ||||
| 
 | ||||
| 	void USBDevice::handle_input_data(BAN::ConstByteSpan data, uint8_t endpoint_id) | ||||
| 	{ | ||||
| 		if (m_class_driver) | ||||
| 			m_class_driver->handle_input_data(data, endpoint_id); | ||||
| 	} | ||||
| 
 | ||||
| 	USB::SpeedClass USBDevice::determine_speed_class(uint64_t bits_per_second) | ||||
| 	{ | ||||
| 		if (bits_per_second <= 1'500'000) | ||||
|  |  | |||
|  | @ -0,0 +1,734 @@ | |||
| #include <BAN/ByteSpan.h> | ||||
| 
 | ||||
| #include <kernel/FS/DevFS/FileSystem.h> | ||||
| #include <kernel/USB/HID/HIDDriver.h> | ||||
| 
 | ||||
| #define DEBUG_HID 0 | ||||
| 
 | ||||
| namespace Kernel | ||||
| { | ||||
| 
 | ||||
| 	enum class HIDDescriptorType : uint8_t | ||||
| 	{ | ||||
| 		HID      = 0x21, | ||||
| 		Report   = 0x22, | ||||
| 		Physical = 0x23, | ||||
| 	}; | ||||
| 
 | ||||
| 	struct HIDDescriptor | ||||
| 	{ | ||||
| 		uint8_t bLength; | ||||
| 		uint8_t bDescriptorType; | ||||
| 		uint16_t bcdHID; | ||||
| 		uint8_t bCountryCode; | ||||
| 		uint8_t bNumDescriptors; | ||||
| 		struct | ||||
| 		{ | ||||
| 			uint8_t bDescriptorType; | ||||
| 			uint16_t wItemLength; | ||||
| 		} __attribute__((packed)) descriptors[]; | ||||
| 	} __attribute__((packed)); | ||||
| 	static_assert(sizeof(HIDDescriptor) == 6); | ||||
| 
 | ||||
| 
 | ||||
| 	struct GlobalState | ||||
| 	{ | ||||
| 		BAN::Optional<uint16_t> usage_page; | ||||
| 		BAN::Optional<int32_t> logical_minimum; | ||||
| 		BAN::Optional<int32_t> logical_maximum_signed; | ||||
| 		BAN::Optional<int32_t> logical_maximum_unsigned; | ||||
| 		BAN::Optional<int32_t> physical_minimum; | ||||
| 		BAN::Optional<int32_t> physical_maximum; | ||||
| 		// FIXME: support units
 | ||||
| 		BAN::Optional<uint32_t> report_size; | ||||
| 		// FIXME: support report id
 | ||||
| 		BAN::Optional<uint32_t> report_count; | ||||
| 	}; | ||||
| 
 | ||||
| 	struct LocalState | ||||
| 	{ | ||||
| 		BAN::Vector<uint32_t> usage_stack; | ||||
| 		BAN::Optional<uint32_t> usage_minimum; | ||||
| 		BAN::Optional<uint32_t> usage_maximum; | ||||
| 		// FIXME: support all local items
 | ||||
| 	}; | ||||
| 
 | ||||
| 	using namespace USBHID; | ||||
| 
 | ||||
| #if DEBUG_HID | ||||
| 	static void dump_hid_collection(const Collection& collection, size_t indent); | ||||
| #endif | ||||
| 
 | ||||
| 	static BAN::ErrorOr<Collection> parse_report_descriptor(BAN::ConstByteSpan report_data); | ||||
| 
 | ||||
| 	BAN::ErrorOr<BAN::UniqPtr<USBHIDDriver>> USBHIDDriver::create(USBDevice& device, const USBDevice::InterfaceDescriptor& interface, uint8_t interface_index) | ||||
| 	{ | ||||
| 		auto result = TRY(BAN::UniqPtr<USBHIDDriver>::create(device, interface, interface_index)); | ||||
| 		TRY(result->initialize()); | ||||
| 		return result; | ||||
| 	} | ||||
| 
 | ||||
| 	USBHIDDriver::USBHIDDriver(USBDevice& device, const USBDevice::InterfaceDescriptor& interface, uint8_t interface_index) | ||||
| 		: m_device(device) | ||||
| 		, m_interface(interface) | ||||
| 		, m_interface_index(interface_index) | ||||
| 	{} | ||||
| 
 | ||||
| 	USBHIDDriver::~USBHIDDriver() | ||||
| 	{} | ||||
| 
 | ||||
| 	BAN::ErrorOr<void> USBHIDDriver::initialize() | ||||
| 	{ | ||||
| 		auto dma_buffer = TRY(DMARegion::create(1024)); | ||||
| 
 | ||||
| 		ASSERT(static_cast<USB::InterfaceBaseClass>(m_interface.descriptor.bInterfaceClass) == USB::InterfaceBaseClass::HID); | ||||
| 
 | ||||
| 		size_t endpoint_index = static_cast<size_t>(-1); | ||||
| 		for (size_t i = 0; i < m_interface.endpoints.size(); i++) | ||||
| 		{ | ||||
| 			const auto& endpoint = m_interface.endpoints[i]; | ||||
| 			if (!(endpoint.descriptor.bEndpointAddress & 0x80)) | ||||
| 				continue; | ||||
| 			if (endpoint.descriptor.bmAttributes != 0x03) | ||||
| 				continue; | ||||
| 			endpoint_index = i; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (endpoint_index >= m_interface.endpoints.size()) | ||||
| 		{ | ||||
| 			dwarnln("HID device does not contain IN interrupt endpoint"); | ||||
| 			return BAN::Error::from_errno(EFAULT); | ||||
| 		} | ||||
| 
 | ||||
| 		bool hid_descriptor_invalid = false; | ||||
| 		size_t hid_descriptor_index = static_cast<size_t>(-1); | ||||
| 		for (size_t i = 0; i < m_interface.misc_descriptors.size(); i++) | ||||
| 		{ | ||||
| 			if (static_cast<HIDDescriptorType>(m_interface.misc_descriptors[i][1]) != HIDDescriptorType::HID) | ||||
| 				continue; | ||||
| 			if (m_interface.misc_descriptors[i].size() < sizeof(HIDDescriptor)) | ||||
| 				hid_descriptor_invalid = true; | ||||
| 			const auto& hid_descriptor = *reinterpret_cast<const HIDDescriptor*>(m_interface.misc_descriptors[i].data()); | ||||
| 			if (hid_descriptor.bLength != m_interface.misc_descriptors[i].size()) | ||||
| 				hid_descriptor_invalid = true; | ||||
| 			if (hid_descriptor.bLength != sizeof(HIDDescriptor) + hid_descriptor.bNumDescriptors * 3) | ||||
| 				hid_descriptor_invalid = true; | ||||
| 			hid_descriptor_index = i; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (hid_descriptor_index >= m_interface.misc_descriptors.size()) | ||||
| 		{ | ||||
| 			dwarnln("HID device does not contain HID descriptor"); | ||||
| 			return BAN::Error::from_errno(EFAULT); | ||||
| 		} | ||||
| 		if (hid_descriptor_invalid) | ||||
| 		{ | ||||
| 			dwarnln("HID device contains an invalid HID descriptor"); | ||||
| 			return BAN::Error::from_errno(EFAULT); | ||||
| 		} | ||||
| 
 | ||||
| 		// If this device supports boot protocol, make sure it is not used
 | ||||
| 		if (m_interface.endpoints.front().descriptor.bDescriptorType & 0x80) | ||||
| 		{ | ||||
| 			USBDeviceRequest request; | ||||
| 			request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Class | USB::RequestType::Interface; | ||||
| 			request.bRequest      = USB::Request::SET_INTERFACE; | ||||
| 			request.wValue        = 1; // report protocol
 | ||||
| 			request.wIndex        = m_interface_index; | ||||
| 			request.wLength       = 0; | ||||
| 			TRY(m_device.send_request(request, 0)); | ||||
| 		} | ||||
| 
 | ||||
| 		Collection collection {}; | ||||
| 
 | ||||
| 		const auto& hid_descriptor = *reinterpret_cast<const HIDDescriptor*>(m_interface.misc_descriptors[hid_descriptor_index].data()); | ||||
| 		dprintln_if(DEBUG_HID, "HID descriptor ({} bytes)", m_interface.misc_descriptors[hid_descriptor_index].size()); | ||||
| 		dprintln_if(DEBUG_HID, "  bLength:         {}",       hid_descriptor.bLength); | ||||
| 		dprintln_if(DEBUG_HID, "  bDescriptorType: {}",       hid_descriptor.bDescriptorType); | ||||
| 		dprintln_if(DEBUG_HID, "  bcdHID:          {H}.{2H}", hid_descriptor.bcdHID >> 8, hid_descriptor.bcdHID & 0xFF); | ||||
| 		dprintln_if(DEBUG_HID, "  bCountryCode:    {}",       hid_descriptor.bCountryCode); | ||||
| 		dprintln_if(DEBUG_HID, "  bNumDescriptors: {}",       hid_descriptor.bNumDescriptors); | ||||
| 
 | ||||
| 		bool report_descriptor_parsed = false; | ||||
| 		for (size_t i = 0; i < hid_descriptor.bNumDescriptors; i++) | ||||
| 		{ | ||||
| 			auto descriptor = hid_descriptor.descriptors[i]; | ||||
| 
 | ||||
| 			if (static_cast<HIDDescriptorType>(descriptor.bDescriptorType) != HIDDescriptorType::Report) | ||||
| 			{ | ||||
| 				dprintln_if(DEBUG_HID, "Skipping HID descriptor type 0x{2H}", descriptor.bDescriptorType); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (report_descriptor_parsed) | ||||
| 			{ | ||||
| 				dwarnln("Multiple report descriptors specified"); | ||||
| 				return BAN::Error::from_errno(ENOTSUP); | ||||
| 			} | ||||
| 
 | ||||
| 			if (descriptor.wItemLength > dma_buffer->size()) | ||||
| 			{ | ||||
| 				dwarnln("Too big report descriptor size {} bytes ({} supported)", +descriptor.wItemLength, dma_buffer->size()); | ||||
| 				return BAN::Error::from_errno(ENOBUFS); | ||||
| 			} | ||||
| 
 | ||||
| 			{ | ||||
| 				USBDeviceRequest request; | ||||
| 				request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Standard | USB::RequestType::Interface; | ||||
| 				request.bRequest      = USB::Request::GET_DESCRIPTOR; | ||||
| 				request.wValue        = static_cast<uint16_t>(HIDDescriptorType::Report) << 8; | ||||
| 				request.wIndex        = m_interface_index; | ||||
| 				request.wLength       = descriptor.wItemLength; | ||||
| 				auto transferred = TRY(m_device.send_request(request, dma_buffer->paddr())); | ||||
| 
 | ||||
| 				if (transferred < descriptor.wItemLength) | ||||
| 				{ | ||||
| 					dwarnln("HID device did not respond with full report descriptor"); | ||||
| 					return BAN::Error::from_errno(EFAULT); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			dprintln_if(DEBUG_HID, "Parsing {} byte report descriptor", +descriptor.wItemLength); | ||||
| 
 | ||||
| 			auto report_data = BAN::ConstByteSpan(reinterpret_cast<uint8_t*>(dma_buffer->vaddr()), descriptor.wItemLength); | ||||
| 			collection = TRY(parse_report_descriptor(report_data)); | ||||
| 
 | ||||
| 			report_descriptor_parsed = true; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!report_descriptor_parsed) | ||||
| 		{ | ||||
| 			dwarnln("No report descriptors specified"); | ||||
| 			return BAN::Error::from_errno(EFAULT); | ||||
| 		} | ||||
| 
 | ||||
| 		if (collection.usage_page != 0x01) | ||||
| 		{ | ||||
| 			dwarnln("Top most collection is not generic desktop page"); | ||||
| 			return BAN::Error::from_errno(EFAULT); | ||||
| 		} | ||||
| 
 | ||||
| #if DEBUG_HID | ||||
| 		{ | ||||
| 			SpinLockGuard _(Debug::s_debug_lock); | ||||
| 			dump_hid_collection(collection, 0); | ||||
| 		} | ||||
| #endif | ||||
| 
 | ||||
| 		switch (collection.usage_id) | ||||
| 		{ | ||||
| 			default: | ||||
| 				dwarnln("Unsupported generic descript page usage 0x{2H}", collection.usage_id); | ||||
| 				return BAN::Error::from_errno(ENOTSUP); | ||||
| 		} | ||||
| 		DevFileSystem::get().add_device(m_hid_device); | ||||
| 
 | ||||
| 		const auto& endpoint_descriptor = m_interface.endpoints[endpoint_index].descriptor; | ||||
| 
 | ||||
| 		m_endpoint_id = (endpoint_descriptor.bEndpointAddress & 0x0F) * 2 + !!(endpoint_descriptor.bEndpointAddress & 0x80); | ||||
| 		m_collection = BAN::move(collection); | ||||
| 
 | ||||
| 		TRY(m_device.initialize_endpoint(endpoint_descriptor)); | ||||
| 
 | ||||
| 		return {}; | ||||
| 	} | ||||
| 
 | ||||
| 	void USBHIDDriver::forward_collection_inputs(const Collection& collection, BAN::ConstByteSpan& data, size_t bit_offset) | ||||
| 	{ | ||||
| 		const auto extract_bits = | ||||
| 			[data](size_t bit_offset, size_t bit_count, bool as_unsigned) -> int64_t | ||||
| 			{ | ||||
| 				if (bit_offset >= data.size() * 8) | ||||
| 					return 0; | ||||
| 				if (bit_count + bit_offset > data.size() * 8) | ||||
| 					bit_count = data.size() * 8 - bit_offset; | ||||
| 
 | ||||
| 				uint32_t result = 0; | ||||
| 				uint32_t result_offset = 0; | ||||
| 
 | ||||
| 				while (result_offset < bit_count) | ||||
| 				{ | ||||
| 					const uint32_t byte  = bit_offset / 8; | ||||
| 					const uint32_t bit   = bit_offset % 8; | ||||
| 					const uint32_t count = BAN::Math::min<uint32_t>(bit_count - result_offset, 8 - bit); | ||||
| 					const uint32_t mask  = (1 << count) - 1; | ||||
| 
 | ||||
| 					result |= static_cast<uint32_t>((data[byte] >> bit) & mask) << result_offset; | ||||
| 
 | ||||
| 					bit_offset    += count; | ||||
| 					result_offset += count; | ||||
| 				} | ||||
| 
 | ||||
| 				if (!as_unsigned && (result & (1u << (bit_count - 1)))) | ||||
| 				{ | ||||
| 					const uint32_t mask = (1u << bit_count) - 1; | ||||
| 					return -(static_cast<int64_t>(~result & mask) + 1); | ||||
| 				} | ||||
| 
 | ||||
| 				return result; | ||||
| 			}; | ||||
| 
 | ||||
| 		for (const auto& entry : collection.entries) | ||||
| 		{ | ||||
| 			if (entry.has<Collection>()) | ||||
| 			{ | ||||
| 				forward_collection_inputs(entry.get<Collection>(), data, bit_offset); | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			ASSERT(entry.has<Report>()); | ||||
| 			const auto& input = entry.get<Report>(); | ||||
| 			if (input.type != Report::Type::Input) | ||||
| 				continue; | ||||
| 
 | ||||
| 			ASSERT(input.report_size <= 32); | ||||
| 
 | ||||
| 			if (input.usage_id == 0 && input.usage_minimum == 0 && input.usage_maximum == 0) | ||||
| 			{ | ||||
| 				bit_offset += input.report_size * input.report_count; | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			for (uint32_t i = 0; i < input.report_count; i++) | ||||
| 			{ | ||||
| 				const int64_t logical = extract_bits(bit_offset, input.report_size, input.logical_minimum >= 0); | ||||
| 				if (logical < input.logical_minimum || logical > input.logical_maximum) | ||||
| 				{ | ||||
| 					bit_offset += input.report_size; | ||||
| 					continue; | ||||
| 				} | ||||
| 
 | ||||
| 				const int64_t physical = | ||||
| 					(input.physical_maximum - input.physical_minimum) * | ||||
| 					(logical - input.logical_minimum) / | ||||
| 					(input.logical_maximum - input.logical_minimum) + | ||||
| 					input.physical_minimum; | ||||
| 
 | ||||
| 				const uint32_t usage_base = input.usage_id ? input.usage_id : input.usage_minimum; | ||||
| 				if (input.flags & 0x02) | ||||
| 					m_hid_device->handle_variable(input.usage_page, usage_base + i, physical); | ||||
| 				else | ||||
| 					m_hid_device->handle_array(input.usage_page, usage_base + physical); | ||||
| 
 | ||||
| 				bit_offset += input.report_size; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void USBHIDDriver::handle_input_data(BAN::ConstByteSpan data, uint8_t endpoint_id) | ||||
| 	{ | ||||
| 		ASSERT(m_endpoint_id == endpoint_id); | ||||
| 
 | ||||
| 		if constexpr(DEBUG_HID) | ||||
| 		{ | ||||
| 			const auto nibble_to_hex = [](uint8_t x) -> char { return x + (x < 10 ? '0' : 'A' - 10); }; | ||||
| 
 | ||||
| 			char buffer[512]; | ||||
| 			char* ptr = buffer; | ||||
| 			for (size_t i = 0; i < BAN::Math::min<size_t>((sizeof(buffer) - 1) / 3, data.size()); i++) | ||||
| 			{ | ||||
| 				*ptr++ = nibble_to_hex(data[i] >> 4); | ||||
| 				*ptr++ = nibble_to_hex(data[i] & 0xF); | ||||
| 				*ptr++ = ' '; | ||||
| 			} | ||||
| 			*ptr = '\0'; | ||||
| 
 | ||||
| 			dprintln_if(DEBUG_HID, "Received {} bytes from endpoint {}: {}", data.size(), endpoint_id, buffer); | ||||
| 		} | ||||
| 
 | ||||
| 		m_hid_device->start_report(); | ||||
| 		forward_collection_inputs(m_collection, data, 0); | ||||
| 		m_hid_device->stop_report(); | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<Collection> parse_report_descriptor(BAN::ConstByteSpan report_data) | ||||
| 	{ | ||||
| 		BAN::Vector<GlobalState> global_stack; | ||||
| 		GlobalState global_state; | ||||
| 
 | ||||
| 		LocalState local_state; | ||||
| 
 | ||||
| 		BAN::Optional<Collection> result; | ||||
| 		BAN::Vector<Collection> collection_stack; | ||||
| 
 | ||||
| 		const auto extract_report_item = | ||||
| 			[&](bool as_unsigned) -> int64_t | ||||
| 			{ | ||||
| 				uint32_t value = 0; | ||||
| 				auto value_data = report_data.slice(1); | ||||
| 				switch (report_data[0] & 0x03) | ||||
| 				{ | ||||
| 					case 1: value = as_unsigned ? value_data.as<const uint8_t>()  : value_data.as<const int8_t>();  break; | ||||
| 					case 2: value = as_unsigned ? value_data.as<const uint16_t>() : value_data.as<const int16_t>(); break; | ||||
| 					case 3: value = as_unsigned ? value_data.as<const uint32_t>() : value_data.as<const int32_t>(); break; | ||||
| 				} | ||||
| 				return value; | ||||
| 			}; | ||||
| 
 | ||||
| 		constexpr auto get_correct_sign = | ||||
| 			[](int64_t min, int64_t max_signed, int64_t max_unsigned) -> int64_t | ||||
| 			{ | ||||
| 				if (min < 0 || max_signed >= 0) | ||||
| 					return max_signed; | ||||
| 				return max_unsigned; | ||||
| 			}; | ||||
| 
 | ||||
| 		const auto add_data_item = | ||||
| 			[&](Report::Type type, uint32_t item_data, BAN::Vector<BAN::Variant<Collection, Report>>& container) -> BAN::ErrorOr<void> | ||||
| 			{ | ||||
| 				if (!global_state.report_count.has_value() || !global_state.report_size.has_value()) | ||||
| 				{ | ||||
| 					dwarnln("Report count and/or report size is not defined"); | ||||
| 					return BAN::Error::from_errno(EFAULT); | ||||
| 				} | ||||
| 				if (!global_state.usage_page.has_value()) | ||||
| 				{ | ||||
| 					dwarnln("Usage page is not defined"); | ||||
| 					return BAN::Error::from_errno(EFAULT); | ||||
| 				} | ||||
| 				if (!global_state.logical_minimum.has_value() || !global_state.logical_maximum_signed.has_value()) | ||||
| 				{ | ||||
| 					dwarnln("Logical minimum and/or logical maximum is not defined"); | ||||
| 					return BAN::Error::from_errno(EFAULT); | ||||
| 				} | ||||
| 				if (global_state.physical_minimum.has_value() != global_state.physical_minimum.has_value()) | ||||
| 				{ | ||||
| 					dwarnln("Only one of physical minimum and physical maximum is defined"); | ||||
| 					return BAN::Error::from_errno(EFAULT); | ||||
| 				} | ||||
| 				if (local_state.usage_minimum.has_value() != local_state.usage_maximum.has_value()) | ||||
| 				{ | ||||
| 					dwarnln("Only one of logical minimum and logical maximum is defined"); | ||||
| 					return BAN::Error::from_errno(EFAULT); | ||||
| 				} | ||||
| 
 | ||||
| 				const int64_t logical_minimum = global_state.logical_minimum.value(); | ||||
| 				const int64_t logical_maximum = get_correct_sign( | ||||
| 					global_state.logical_minimum.value(), | ||||
| 					global_state.logical_maximum_signed.value(), | ||||
| 					global_state.logical_maximum_unsigned.value() | ||||
| 				); | ||||
| 
 | ||||
| 				int64_t physical_minimum = logical_minimum; | ||||
| 				int64_t physical_maximum = logical_maximum; | ||||
| 				if (global_state.physical_minimum.has_value() && (global_state.physical_minimum.value() || global_state.physical_maximum.value())) | ||||
| 				{ | ||||
| 					physical_minimum = global_state.physical_minimum.value(); | ||||
| 					physical_maximum = global_state.physical_maximum.value(); | ||||
| 				} | ||||
| 
 | ||||
| 				if (local_state.usage_stack.empty()) | ||||
| 				{ | ||||
| 					if (local_state.usage_minimum.has_value() && local_state.usage_maximum.has_value()) | ||||
| 					{ | ||||
| 						Report item; | ||||
| 						item.usage_page       = global_state.usage_page.value(); | ||||
| 						item.usage_id         = 0; | ||||
| 						item.usage_minimum    = local_state.usage_minimum.value(); | ||||
| 						item.usage_maximum    = local_state.usage_maximum.value(); | ||||
| 						item.type             = type; | ||||
| 						item.report_count     = global_state.report_count.value(); | ||||
| 						item.report_size      = global_state.report_size.value(); | ||||
| 						item.logical_minimum  = logical_minimum; | ||||
| 						item.logical_maximum  = logical_maximum; | ||||
| 						item.physical_minimum = physical_minimum; | ||||
| 						item.physical_maximum = physical_maximum; | ||||
| 						item.flags            = item_data; | ||||
| 						TRY(container.push_back(item)); | ||||
| 
 | ||||
| 						return {}; | ||||
| 					} | ||||
| 
 | ||||
| 					Report item; | ||||
| 					item.usage_page       = global_state.usage_page.value(); | ||||
| 					item.usage_id         = 0; | ||||
| 					item.usage_minimum    = 0; | ||||
| 					item.usage_maximum    = 0; | ||||
| 					item.type             = type; | ||||
| 					item.report_count     = global_state.report_count.value(); | ||||
| 					item.report_size      = global_state.report_size.value(); | ||||
| 					item.logical_minimum  = 0; | ||||
| 					item.logical_maximum  = 0; | ||||
| 					item.physical_minimum = 0; | ||||
| 					item.physical_maximum = 0; | ||||
| 					item.flags            = item_data; | ||||
| 					TRY(container.push_back(item)); | ||||
| 
 | ||||
| 					return {}; | ||||
| 				} | ||||
| 
 | ||||
| 				for (size_t i = 0; i < global_state.report_count.value(); i++) | ||||
| 				{ | ||||
| 					const uint32_t usage = local_state.usage_stack[BAN::Math::min<size_t>(i, local_state.usage_stack.size() - 1)]; | ||||
| 
 | ||||
| 					Report item; | ||||
| 					item.usage_page       = (usage >> 16) ? (usage >> 16) : global_state.usage_page.value(); | ||||
| 					item.usage_id         = usage & 0xFFFF; | ||||
| 					item.usage_minimum    = 0; | ||||
| 					item.usage_maximum    = 0; | ||||
| 					item.type             = type; | ||||
| 					item.report_count     = 1; | ||||
| 					item.report_size      = global_state.report_size.value(); | ||||
| 					item.logical_minimum  = logical_minimum; | ||||
| 					item.logical_maximum  = logical_maximum; | ||||
| 					item.physical_minimum = physical_minimum; | ||||
| 					item.physical_maximum = physical_maximum; | ||||
| 					item.flags            = item_data; | ||||
| 					TRY(container.push_back(item)); | ||||
| 				} | ||||
| 
 | ||||
| 				return {}; | ||||
| 			}; | ||||
| 
 | ||||
| 		while (report_data.size() > 0) | ||||
| 		{ | ||||
| 			const uint8_t item_size =  report_data[0]       & 0x03; | ||||
| 			const uint8_t item_type = (report_data[0] >> 2) & 0x03; | ||||
| 			const uint8_t item_tag  = (report_data[0] >> 4) & 0x0F; | ||||
| 
 | ||||
| 			if (item_type == 0) | ||||
| 			{ | ||||
| 				switch (item_tag) | ||||
| 				{ | ||||
| 					case 0b1000: // input
 | ||||
| 						if (collection_stack.empty()) | ||||
| 						{ | ||||
| 							dwarnln("Invalid input item outside of collection"); | ||||
| 							return BAN::Error::from_errno(EFAULT); | ||||
| 						} | ||||
| 						TRY(add_data_item(Report::Type::Input, extract_report_item(true), collection_stack.back().entries)); | ||||
| 						break; | ||||
| 					case 0b1001: // output
 | ||||
| 						if (collection_stack.empty()) | ||||
| 						{ | ||||
| 							dwarnln("Invalid input item outside of collection"); | ||||
| 							return BAN::Error::from_errno(EFAULT); | ||||
| 						} | ||||
| 						TRY(add_data_item(Report::Type::Output, extract_report_item(true), collection_stack.back().entries)); | ||||
| 						break; | ||||
| 					case 0b1011: // feature
 | ||||
| 						if (collection_stack.empty()) | ||||
| 						{ | ||||
| 							dwarnln("Invalid input item outside of collection"); | ||||
| 							return BAN::Error::from_errno(EFAULT); | ||||
| 						} | ||||
| 						TRY(add_data_item(Report::Type::Feature, extract_report_item(true), collection_stack.back().entries)); | ||||
| 						break; | ||||
| 					case 0b1010: // collection
 | ||||
| 					{ | ||||
| 						if (local_state.usage_stack.size() != 1) | ||||
| 						{ | ||||
| 							dwarnln("{} usages specified for collection", local_state.usage_stack.empty() ? "No" : "Multiple"); | ||||
| 							return BAN::Error::from_errno(EFAULT); | ||||
| 						} | ||||
| 						uint16_t usage_page = 0; | ||||
| 						if (global_state.usage_page.has_value()) | ||||
| 							usage_page = global_state.usage_page.value(); | ||||
| 						if (local_state.usage_stack.front() >> 16) | ||||
| 							usage_page = local_state.usage_stack.front() >> 16; | ||||
| 						if (usage_page == 0) | ||||
| 						{ | ||||
| 							dwarnln("Usage page not specified for a collection"); | ||||
| 							return BAN::Error::from_errno(EFAULT); | ||||
| 						} | ||||
| 
 | ||||
| 						TRY(collection_stack.emplace_back()); | ||||
| 						collection_stack.back().usage_page = usage_page; | ||||
| 						collection_stack.back().usage_id   = local_state.usage_stack.front(); | ||||
| 						break; | ||||
| 					} | ||||
| 					case 0b1100: // end collection
 | ||||
| 						if (collection_stack.empty()) | ||||
| 						{ | ||||
| 							dwarnln("End collection outside of collection"); | ||||
| 							return BAN::Error::from_errno(EFAULT); | ||||
| 						} | ||||
| 						if (collection_stack.size() == 1) | ||||
| 						{ | ||||
| 							if (result.has_value()) | ||||
| 							{ | ||||
| 								dwarnln("Multiple top-level collections not supported"); | ||||
| 								return BAN::Error::from_errno(ENOTSUP); | ||||
| 							} | ||||
| 							result = BAN::move(collection_stack.back()); | ||||
| 							collection_stack.pop_back(); | ||||
| 						} | ||||
| 						else | ||||
| 						{ | ||||
| 							TRY(collection_stack[collection_stack.size() - 2].entries.push_back(BAN::move(collection_stack.back()))); | ||||
| 							collection_stack.pop_back(); | ||||
| 						} | ||||
| 						break; | ||||
| 					default: | ||||
| 						dwarnln("Report has reserved main item tag 0b{4b}", item_tag); | ||||
| 						return BAN::Error::from_errno(EFAULT); | ||||
| 				} | ||||
| 
 | ||||
| 				local_state = LocalState(); | ||||
| 			} | ||||
| 			else if (item_type == 1) | ||||
| 			{ | ||||
| 				switch (item_tag) | ||||
| 				{ | ||||
| 					case 0b0000: // usage page
 | ||||
| 						global_state.usage_page = extract_report_item(true); | ||||
| 						break; | ||||
| 					case 0b0001: // logical minimum
 | ||||
| 						global_state.logical_minimum = extract_report_item(false); | ||||
| 						break; | ||||
| 					case 0b0010: // logical maximum
 | ||||
| 						global_state.logical_maximum_signed = extract_report_item(false); | ||||
| 						global_state.logical_maximum_unsigned = extract_report_item(true); | ||||
| 						break; | ||||
| 					case 0b0011: // physical minimum
 | ||||
| 						global_state.physical_minimum = extract_report_item(false); | ||||
| 						break; | ||||
| 					case 0b0100: // physical maximum
 | ||||
| 						global_state.physical_maximum = extract_report_item(false); | ||||
| 						break; | ||||
| 					case 0b0101: // unit exponent
 | ||||
| 						dwarnln("Report units are not supported"); | ||||
| 						return BAN::Error::from_errno(ENOTSUP); | ||||
| 					case 0b0110: // unit
 | ||||
| 						dwarnln("Report units are not supported"); | ||||
| 						return BAN::Error::from_errno(ENOTSUP); | ||||
| 					case 0b0111: // report size
 | ||||
| 						global_state.report_size = extract_report_item(true); | ||||
| 						break; | ||||
| 					case 0b1000: // report id
 | ||||
| 						dwarnln("Report IDs are not supported"); | ||||
| 						return BAN::Error::from_errno(ENOTSUP); | ||||
| 					case 0b1001: // report count
 | ||||
| 						global_state.report_count = extract_report_item(true); | ||||
| 						break; | ||||
| 					case 0b1010: // push
 | ||||
| 						TRY(global_stack.push_back(global_state)); | ||||
| 						break; | ||||
| 					case 0b1011: // pop
 | ||||
| 						if (global_stack.empty()) | ||||
| 						{ | ||||
| 							dwarnln("Report pop from empty stack"); | ||||
| 							return BAN::Error::from_errno(EFAULT); | ||||
| 						} | ||||
| 						global_state = global_stack.back(); | ||||
| 						global_stack.pop_back(); | ||||
| 						break; | ||||
| 					default: | ||||
| 						dwarnln("Report has reserved global item tag 0b{4b}", item_tag); | ||||
| 						return BAN::Error::from_errno(EFAULT); | ||||
| 				} | ||||
| 			} | ||||
| 			else if (item_type == 2) | ||||
| 			{ | ||||
| 				switch (item_tag) | ||||
| 				{ | ||||
| 					case 0b0000: // usage
 | ||||
| 						TRY(local_state.usage_stack.emplace_back(extract_report_item(true))); | ||||
| 						break; | ||||
| 					case 0b0001: // usage minimum
 | ||||
| 						local_state.usage_minimum = extract_report_item(true); | ||||
| 						break; | ||||
| 					case 0b0010: // usage maximum
 | ||||
| 						local_state.usage_maximum = extract_report_item(true); | ||||
| 						break; | ||||
| 					case 0b0011: // designator index
 | ||||
| 					case 0b0100: // designator minimum
 | ||||
| 					case 0b0101: // designator maximum
 | ||||
| 					case 0b0111: // string index
 | ||||
| 					case 0b1000: // string minimum
 | ||||
| 					case 0b1001: // string maximum
 | ||||
| 					case 0b1010: // delimeter
 | ||||
| 						dwarnln("Unsupported local item tag 0b{4b}", item_tag); | ||||
| 						return BAN::Error::from_errno(ENOTSUP); | ||||
| 					default: | ||||
| 						dwarnln("Report has reserved local item tag 0b{4b}", item_tag); | ||||
| 						return BAN::Error::from_errno(EFAULT); | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				dwarnln("Report has reserved item type 0b{2b}", item_type); | ||||
| 				return BAN::Error::from_errno(EFAULT); | ||||
| 			} | ||||
| 
 | ||||
| 			report_data = report_data.slice(1 + item_size); | ||||
| 		} | ||||
| 
 | ||||
| 		if (!result.has_value()) | ||||
| 		{ | ||||
| 			dwarnln("No collection defined in report descriptor"); | ||||
| 			return BAN::Error::from_errno(EFAULT); | ||||
| 		} | ||||
| 
 | ||||
| 		return result.release_value(); | ||||
| 	} | ||||
| 
 | ||||
| #if DEBUG_HID | ||||
| 	static void print_indent(size_t indent) | ||||
| 	{ | ||||
| 		for (size_t i = 0; i < indent; i++) | ||||
| 			Debug::putchar(' '); | ||||
| 	} | ||||
| 
 | ||||
| 	static void dump_hid_report(const Report& report, size_t indent) | ||||
| 	{ | ||||
| 		const char* report_type = ""; | ||||
| 		switch (report.type) | ||||
| 		{ | ||||
| 			case Report::Type::Input:   report_type = "input";   break; | ||||
| 			case Report::Type::Output:  report_type = "output";  break; | ||||
| 			case Report::Type::Feature: report_type = "feature"; break; | ||||
| 		} | ||||
| 		print_indent(indent); | ||||
| 		BAN::Formatter::println(Debug::putchar, "report {}", report_type); | ||||
| 
 | ||||
| 		print_indent(indent + 4); | ||||
| 		BAN::Formatter::println(Debug::putchar, "usage page: {2H}", report.usage_page); | ||||
| 
 | ||||
| 		if (report.usage_id || report.usage_minimum || report.usage_maximum) | ||||
| 		{ | ||||
| 			print_indent(indent + 4); | ||||
| 			if (report.usage_id) | ||||
| 				BAN::Formatter::println(Debug::putchar, "usage:      {2H}", report.usage_id); | ||||
| 			else | ||||
| 				BAN::Formatter::println(Debug::putchar, "usage:      {2H}->{2H}", report.usage_minimum, report.usage_maximum); | ||||
| 		} | ||||
| 
 | ||||
| 		print_indent(indent + 4); | ||||
| 		BAN::Formatter::println(Debug::putchar, "flags:      0b{8b}", report.flags); | ||||
| 
 | ||||
| 		print_indent(indent + 4); | ||||
| 		BAN::Formatter::println(Debug::putchar, "size:       {}", report.report_size); | ||||
| 		print_indent(indent + 4); | ||||
| 		BAN::Formatter::println(Debug::putchar, "count:      {}", report.report_count); | ||||
| 
 | ||||
| 		print_indent(indent + 4); | ||||
| 		BAN::Formatter::println(Debug::putchar, "lminimum:   {}", report.logical_minimum); | ||||
| 		print_indent(indent + 4); | ||||
| 		BAN::Formatter::println(Debug::putchar, "lmaximum:   {}", report.logical_maximum); | ||||
| 
 | ||||
| 		print_indent(indent + 4); | ||||
| 		BAN::Formatter::println(Debug::putchar, "pminimum:   {}", report.physical_minimum); | ||||
| 		print_indent(indent + 4); | ||||
| 		BAN::Formatter::println(Debug::putchar, "pmaximum:   {}", report.physical_maximum); | ||||
| 	} | ||||
| 
 | ||||
| 	static void dump_hid_collection(const Collection& collection, size_t indent) | ||||
| 	{ | ||||
| 		print_indent(indent); | ||||
| 		BAN::Formatter::println(Debug::putchar, "collection {}", collection.type); | ||||
| 		print_indent(indent); | ||||
| 		BAN::Formatter::println(Debug::putchar, "usage {H}:{H}", collection.usage_page, collection.usage_id); | ||||
| 
 | ||||
| 		for (const auto& entry : collection.entries) | ||||
| 		{ | ||||
| 			if (entry.has<Collection>()) | ||||
| 				dump_hid_collection(entry.get<Collection>(), indent + 4); | ||||
| 			if (entry.has<Report>()) | ||||
| 				dump_hid_report(entry.get<Report>(), indent + 4); | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| } | ||||
|  | @ -253,7 +253,8 @@ namespace Kernel | |||
| 					case 3: | ||||
| 						if (!connection_change) | ||||
| 							continue; | ||||
| 						break; | ||||
| 						dprintln_if(DEBUG_XHCI, "USB 3 devices not supported"); | ||||
| 						continue; | ||||
| 					default: | ||||
| 						continue; | ||||
| 				} | ||||
|  |  | |||
|  | @ -65,14 +65,18 @@ namespace Kernel | |||
| 			auto& slot_context          = *reinterpret_cast<XHCI::SlotContext*>        (m_input_context->vaddr() + 1 * context_size); | ||||
| 			auto& endpoint0_context     = *reinterpret_cast<XHCI::EndpointContext*>    (m_input_context->vaddr() + 2 * context_size); | ||||
| 
 | ||||
| 			memset(&input_control_context, 0, context_size); | ||||
| 			input_control_context.add_context_flags = 0b11; | ||||
| 
 | ||||
| 			slot_context.root_hub_port_number = m_port_id; | ||||
| 			memset(&slot_context, 0, context_size); | ||||
| 			slot_context.route_string         = 0; | ||||
| 			slot_context.root_hub_port_number = m_port_id; | ||||
| 			slot_context.context_entries      = 1; | ||||
| 			slot_context.interrupter_target   = 0; | ||||
| 			slot_context.speed                = speed_id; | ||||
| 			// FIXME: 4.5.2 hub
 | ||||
| 
 | ||||
| 			memset(&endpoint0_context, 0, context_size); | ||||
| 			endpoint0_context.endpoint_type       = XHCI::EndpointType::Control; | ||||
| 			endpoint0_context.max_packet_size     = m_endpoints[0].max_packet_size; | ||||
| 			endpoint0_context.error_count         = 3; | ||||
|  | @ -122,18 +126,21 @@ namespace Kernel | |||
| 
 | ||||
| 		{ | ||||
| 			auto& input_control_context = *reinterpret_cast<XHCI::InputControlContext*>(m_input_context->vaddr() + 0 * context_size); | ||||
| 			auto& slot_context          = *reinterpret_cast<XHCI::SlotContext*>        (m_input_context->vaddr() + 1 * context_size); | ||||
| 			auto& endpoint0_context     = *reinterpret_cast<XHCI::EndpointContext*>    (m_input_context->vaddr() + 2 * context_size); | ||||
| 
 | ||||
| 			input_control_context.add_context_flags = 0b10; | ||||
| 			memset(&input_control_context, 0, context_size); | ||||
| 			input_control_context.add_context_flags = 0b11; | ||||
| 
 | ||||
| 			memset(&slot_context, 0, context_size); | ||||
| 			slot_context.max_exit_latency   = 0; // FIXME:
 | ||||
| 			slot_context.interrupter_target = 0; | ||||
| 
 | ||||
| 			memset(&endpoint0_context, 0, context_size); | ||||
| 			endpoint0_context.endpoint_type       = XHCI::EndpointType::Control; | ||||
| 			endpoint0_context.max_packet_size     = m_endpoints[0].max_packet_size; | ||||
| 			endpoint0_context.max_burst_size      = 0; | ||||
| 			endpoint0_context.tr_dequeue_pointer  = (m_endpoints[0].transfer_ring->paddr() + (m_endpoints[0].enqueue_index * sizeof(XHCI::TRB))) | 1; | ||||
| 			endpoint0_context.interval            = 0; | ||||
| 			endpoint0_context.max_primary_streams = 0; | ||||
| 			endpoint0_context.mult                = 0; | ||||
| 			endpoint0_context.error_count         = 3; | ||||
| 			endpoint0_context.tr_dequeue_pointer  = m_endpoints[0].transfer_ring->paddr() | 1; | ||||
| 		} | ||||
| 
 | ||||
| 		XHCI::TRB evaluate_context { .address_device_command = {} }; | ||||
|  | @ -148,6 +155,114 @@ namespace Kernel | |||
| 		return {}; | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<void> XHCIDevice::initialize_endpoint(const USBEndpointDescriptor& endpoint_descriptor) | ||||
| 	{ | ||||
| 		const uint32_t endpoint_id = (endpoint_descriptor.bEndpointAddress & 0x0F) * 2 + !!(endpoint_descriptor.bEndpointAddress & 0x80); | ||||
| 
 | ||||
| 		auto& endpoint = m_endpoints[endpoint_id - 1]; | ||||
| 		ASSERT(!endpoint.transfer_ring); | ||||
| 
 | ||||
| 		uint32_t last_valid_endpoint_id = endpoint_id; | ||||
| 		for (size_t i = endpoint_id; i < m_endpoints.size(); i++) | ||||
| 			if (m_endpoints[i].transfer_ring) | ||||
| 				last_valid_endpoint_id = i + 1; | ||||
| 
 | ||||
| 		endpoint.transfer_ring   = TRY(DMARegion::create(m_transfer_ring_trb_count * sizeof(XHCI::TRB))); | ||||
| 		endpoint.max_packet_size = endpoint_descriptor.wMaxPacketSize & 0x07FF; | ||||
| 		endpoint.dequeue_index   = 0; | ||||
| 		endpoint.enqueue_index   = 0; | ||||
| 		endpoint.cycle_bit       = 1; | ||||
| 		endpoint.callback        = &XHCIDevice::on_interrupt_endpoint_event; | ||||
| 		endpoint.data_region     = TRY(DMARegion::create(endpoint.max_packet_size)); | ||||
| 
 | ||||
| 		memset(reinterpret_cast<void*>(endpoint.transfer_ring->vaddr()), 0, endpoint.transfer_ring->size()); | ||||
| 
 | ||||
| 		{ | ||||
| 			const uint32_t context_size = m_controller.context_size_set() ? 64 : 32; | ||||
| 
 | ||||
| 			auto& input_control_context = *reinterpret_cast<XHCI::InputControlContext*>(m_input_context->vaddr()); | ||||
| 			auto& slot_context          = *reinterpret_cast<XHCI::SlotContext*>        (m_input_context->vaddr() + context_size); | ||||
| 			auto& endpoint_context      = *reinterpret_cast<XHCI::EndpointContext*>    (m_input_context->vaddr() + (endpoint_id + 1) * context_size); | ||||
| 
 | ||||
| 			memset(&input_control_context, 0, context_size); | ||||
| 			input_control_context.add_context_flags = (1u << endpoint_id) | 1; | ||||
| 
 | ||||
| 			memset(&slot_context, 0, context_size); | ||||
| 			slot_context.context_entries = last_valid_endpoint_id; | ||||
| 			// FIXME: 4.5.2 hub
 | ||||
| 
 | ||||
| 			ASSERT(endpoint_descriptor.bEndpointAddress & 0x80); | ||||
| 			ASSERT((endpoint_descriptor.bmAttributes & 0x03) == 3); | ||||
| 			ASSERT(m_controller.port(m_port_id).revision_major == 2); | ||||
| 
 | ||||
| 			memset(&endpoint_context, 0, context_size); | ||||
| 			endpoint_context.endpoint_type       = XHCI::EndpointType::InterruptIn; | ||||
| 			endpoint_context.max_packet_size     = endpoint.max_packet_size; | ||||
| 			endpoint_context.max_burst_size      = (endpoint_descriptor.wMaxPacketSize >> 11) & 0x0003; | ||||
| 			endpoint_context.mult                = 0; | ||||
| 			endpoint_context.error_count         = 3; | ||||
| 			endpoint_context.tr_dequeue_pointer  = endpoint.transfer_ring->paddr() | 1; | ||||
| 			const uint32_t max_esit_payload = endpoint_context.max_packet_size * (endpoint_context.max_burst_size + 1); | ||||
| 			endpoint_context.max_esit_payload_lo = max_esit_payload & 0xFFFF; | ||||
| 			endpoint_context.max_esit_payload_hi = max_esit_payload >> 16; | ||||
| 		} | ||||
| 
 | ||||
| 		XHCI::TRB configure_endpoint { .configure_endpoint_command = {} }; | ||||
| 		configure_endpoint.configure_endpoint_command.trb_type              = XHCI::TRBType::ConfigureEndpointCommand; | ||||
| 		configure_endpoint.configure_endpoint_command.input_context_pointer = m_input_context->paddr(); | ||||
| 		configure_endpoint.configure_endpoint_command.deconfigure           = 0; | ||||
| 		configure_endpoint.configure_endpoint_command.slot_id               = m_slot_id; | ||||
| 		TRY(m_controller.send_command(configure_endpoint)); | ||||
| 
 | ||||
| 		auto& trb = *reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr()); | ||||
| 		memset(const_cast<XHCI::TRB*>(&trb), 0, sizeof(XHCI::TRB)); | ||||
| 		trb.normal.trb_type                  = XHCI::TRBType::Normal; | ||||
| 		trb.normal.data_buffer_pointer       = endpoint.data_region->paddr(); | ||||
| 		trb.normal.trb_transfer_length       = endpoint.data_region->size(); | ||||
| 		trb.normal.td_size                   = 0; | ||||
| 		trb.normal.interrupt_target          = 0; | ||||
| 		trb.normal.cycle_bit                 = 1; | ||||
| 		trb.normal.interrupt_on_completion   = 1; | ||||
| 		trb.normal.interrupt_on_short_packet = 1; | ||||
| 		advance_endpoint_enqueue(endpoint, false); | ||||
| 
 | ||||
| 		m_controller.doorbell_reg(m_slot_id) = endpoint_id; | ||||
| 
 | ||||
| 		return {}; | ||||
| 	} | ||||
| 
 | ||||
| 	void XHCIDevice::on_interrupt_endpoint_event(XHCI::TRB trb) | ||||
| 	{ | ||||
| 		ASSERT(trb.trb_type == XHCI::TRBType::TransferEvent); | ||||
| 		if (trb.transfer_event.completion_code != 1 && trb.transfer_event.completion_code != 13) | ||||
| 		{ | ||||
| 			dwarnln("Interrupt endpoint got transfer event with completion code {}", +trb.transfer_event.completion_code); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		const uint32_t endpoint_id = trb.transfer_event.endpoint_id; | ||||
| 		auto& endpoint = m_endpoints[endpoint_id - 1]; | ||||
| 		ASSERT(endpoint.transfer_ring && endpoint.data_region); | ||||
| 
 | ||||
| 		const uint32_t transfer_length = endpoint.max_packet_size - trb.transfer_event.trb_transfer_length; | ||||
| 		auto received_data = BAN::ConstByteSpan(reinterpret_cast<uint8_t*>(endpoint.data_region->vaddr()), transfer_length); | ||||
| 		handle_input_data(received_data, endpoint_id); | ||||
| 
 | ||||
| 		auto& new_trb = *reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr() + endpoint.enqueue_index * sizeof(XHCI::TRB)); | ||||
| 		memset(const_cast<XHCI::TRB*>(&new_trb), 0, sizeof(XHCI::TRB)); | ||||
| 		new_trb.normal.trb_type                  = XHCI::TRBType::Normal; | ||||
| 		new_trb.normal.data_buffer_pointer       = endpoint.data_region->paddr(); | ||||
| 		new_trb.normal.trb_transfer_length       = endpoint.max_packet_size; | ||||
| 		new_trb.normal.td_size                   = 0; | ||||
| 		new_trb.normal.interrupt_target          = 0; | ||||
| 		new_trb.normal.cycle_bit                 = endpoint.cycle_bit; | ||||
| 		new_trb.normal.interrupt_on_completion   = 1; | ||||
| 		new_trb.normal.interrupt_on_short_packet = 1; | ||||
| 		advance_endpoint_enqueue(endpoint, false); | ||||
| 
 | ||||
| 		m_controller.doorbell_reg(m_slot_id) = endpoint_id; | ||||
| 	} | ||||
| 
 | ||||
| 	void XHCIDevice::on_transfer_event(const volatile XHCI::TRB& trb) | ||||
| 	{ | ||||
| 		ASSERT(trb.trb_type == XHCI::TRBType::TransferEvent); | ||||
|  | @ -157,10 +272,22 @@ namespace Kernel | |||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		auto& endpoint = m_endpoints[trb.transfer_event.endpoint_id - 1]; | ||||
| 
 | ||||
| 		if (endpoint.callback) | ||||
| 		{ | ||||
| 			XHCI::TRB copy; | ||||
| 			copy.raw.dword0 = trb.raw.dword0; | ||||
| 			copy.raw.dword1 = trb.raw.dword1; | ||||
| 			copy.raw.dword2 = trb.raw.dword2; | ||||
| 			copy.raw.dword3 = trb.raw.dword3; | ||||
| 			(this->*endpoint.callback)(copy); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		// Get received bytes from short packet
 | ||||
| 		if (trb.transfer_event.completion_code == 13) | ||||
| 		{ | ||||
| 			auto& endpoint = m_endpoints[trb.transfer_event.endpoint_id - 1]; | ||||
| 			auto* transfer_trb_arr = reinterpret_cast<volatile XHCI::TRB*>(endpoint.transfer_ring->vaddr()); | ||||
| 
 | ||||
| 			const uint32_t trb_index = (trb.transfer_event.trb_pointer - endpoint.transfer_ring->paddr()) / sizeof(XHCI::TRB); | ||||
|  | @ -179,7 +306,7 @@ namespace Kernel | |||
| 		} | ||||
| 
 | ||||
| 		// NOTE: dword2 is last (and atomic) as that is what send_request is waiting for
 | ||||
| 		auto& completion_trb = m_endpoints[trb.transfer_event.endpoint_id - 1].completion_trb; | ||||
| 		auto& completion_trb = endpoint.completion_trb; | ||||
| 		completion_trb.raw.dword0 = trb.raw.dword0; | ||||
| 		completion_trb.raw.dword1 = trb.raw.dword1; | ||||
| 		completion_trb.raw.dword3 = trb.raw.dword3; | ||||
|  |  | |||
|  | @ -213,6 +213,9 @@ static void init2(void*) | |||
| 	PCI::PCIManager::get().initialize_devices(); | ||||
| 	dprintln("PCI devices initialized"); | ||||
| 
 | ||||
| 	// FIXME: This is very hacky way to wait until USB stack is initialized
 | ||||
| 	SystemTimer::get().sleep(500); | ||||
| 
 | ||||
| 	VirtualFileSystem::initialize(cmdline.root); | ||||
| 	dprintln("VFS initialized"); | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,5 +31,6 @@ qemu-system-$QEMU_ARCH												\ | |||
| 	-drive format=raw,id=disk,file=${BANAN_DISK_IMAGE_PATH},if=none	\ | ||||
| 	-device e1000e,netdev=net										\ | ||||
| 	-netdev user,id=net												\ | ||||
| 	-device qemu-xhci												\ | ||||
| 	$DISK_ARGS														\ | ||||
| 	$@																\ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue