/* * Copyright (c) 2021, Jesse Buhagiar * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include namespace Kernel { class SysFSUSBDeviceInformation; } namespace Kernel::USB { class USBController; class USBConfiguration; // // Some nice info from FTDI on device enumeration and how some of this // glues together: // // https://www.ftdichip.com/Support/Documents/TechnicalNotes/TN_113_Simplified%20Description%20of%20USB%20Device%20Enumeration.pdf class Hub; class Device : public AtomicRefCounted { friend class Hub; // Note: This constructor is only used by the USB::Hub class // As that class inherits from Device, but is usually initialized with a Device object // needing a copy constructor, // FIXME: Ideally Hub should not inherit from Device, but instead have a Device member Device(Device const& device); public: enum class DeviceSpeed : u8 { LowSpeed = 0, FullSpeed, HighSpeed, SuperSpeed, }; static ErrorOr> try_create(USBController&, Hub const&, u8, DeviceSpeed); Device(USBController const&, Hub const*, u8 port, DeviceSpeed); Device(USBController const&, Hub const*, u8 port, DeviceSpeed, u8 address, USBDeviceDescriptor const& descriptor); virtual ~Device(); u8 port() const { return m_device_port; } DeviceSpeed speed() const { return m_device_speed; } u8 address() const { return m_address; } USBDeviceDescriptor const& device_descriptor() const { return m_device_descriptor; } USBController& controller() { return *m_controller; } USBController const& controller() const { return *m_controller; } Hub const* hub() const { return m_hub; } ErrorOr control_transfer(u8 request_type, u8 request, u16 value, u16 index, u16 length, void* data); Vector const& configurations() const { return m_configurations; } void set_driver(Driver& driver) { m_driver = driver; } void detach() { if (m_driver) m_driver->detach(*this); m_driver = nullptr; } ErrorOr set_configuration_and_interface(USBInterface const& interface); SpinlockProtected, LockRank::None>& sysfs_device_info_node(Badge) { return m_sysfs_device_info_node; } template Controller> void set_max_packet_size(Badge, u8 max_packet_size) { m_default_pipe->set_max_packet_size(max_packet_size); } template Controller> void set_address(Badge, u8 address) { VERIFY(m_address == 0); // Device can only transition once m_address = address; } template Controller> void set_descriptor(Badge, USBDeviceDescriptor const& descriptor) { memcpy(&m_device_descriptor, &descriptor, sizeof(USBDeviceDescriptor)); } template Controller> Vector& configurations(Badge) { return m_configurations; } template Controller> void set_controller_identifier(Badge, size_t identifier) { m_controller_identifier = identifier; } size_t controller_identifier() const { return m_controller_identifier; } protected: void set_default_pipe(NonnullOwnPtr pipe); ErrorOr set_configuration(USBConfiguration const& configuration); u8 m_device_port { 0 }; // What port is this device attached to. NOTE: This is 1-based. DeviceSpeed m_device_speed; // What speed is this device running at u8 m_address { 0 }; // USB address assigned to this device size_t m_controller_identifier { 0 }; // Device description u16 m_vendor_id { 0 }; // This device's vendor ID assigned by the USB group u16 m_product_id { 0 }; // This device's product ID assigned by the USB group USBDeviceDescriptor m_device_descriptor {}; // Device Descriptor obtained from USB Device Vector m_configurations; // Configurations for this device NonnullLockRefPtr m_controller; Hub const* m_hub { nullptr }; OwnPtr m_default_pipe; // Default communication pipe (endpoint0) used during enumeration // The current configuration is behind a SetOnce, this is the easiest way to // guarantee that when a driver is attached, another driver cannot choose a different configuration // using a different interface in the same configuration is fine, though SetOnce m_was_configured; u8 m_current_configuration { 0 }; LockRefPtr m_driver; private: IntrusiveListNode> m_hub_child_node; protected: SpinlockProtected, LockRank::None> m_sysfs_device_info_node; public: using List = IntrusiveList<&Device::m_hub_child_node>; }; }