| #ifndef MOTORS_USB_USB_H_ |
| #define MOTORS_USB_USB_H_ |
| |
| #include <assert.h> |
| #include <string.h> |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "aos/macros.h" |
| #include "motors/core/kinetis.h" |
| #include "motors/usb/constants.h" |
| #include "motors/util.h" |
| |
| namespace frc971::teensy { |
| |
| // A sufficient memory barrier between writing some data and telling the USB |
| // hardware to read it or having the USB hardware say some data is readable and |
| // actually reading it. |
| static inline void dma_memory_barrier() { DmaMemoryBarrier(); } |
| |
| // Aligned for faster access via memcpy etc. |
| // |
| // Also, the Freescale example stack forces aligned buffers to work around some |
| // hardware limitations which may or may not apply to our chips. |
| typedef void *DataPointer __attribute__((aligned(4))); |
| |
| // An entry in the Buffer Descriptor Table. |
| struct BdtEntry { |
| uint32_t buffer_descriptor; |
| DataPointer address; |
| }; |
| |
| #define V_USB_BD_BC(value) \ |
| static_cast<uint32_t>(static_cast<uint32_t>(value) << 16) |
| #define G_USB_BD_BC(bd) (((bd) >> 16) & UINT32_C(0x3FF)) |
| #define M_USB_BD_OWN UINT32_C(1 << 7) |
| #define M_USB_BD_DATA1 UINT32_C(1 << 6) |
| static_assert(static_cast<uint32_t>(Data01::kData1) == M_USB_BD_DATA1, |
| "Wrong value"); |
| #define M_USB_BD_KEEP UINT32_C(1 << 5) |
| #define M_USB_BD_NINC UINT32_C(1 << 4) |
| #define M_USB_BD_DTS UINT32_C(1 << 3) |
| #define M_USB_BD_STALL UINT32_C(1 << 2) |
| #define V_USB_BD_PID(value) \ |
| static_cast<uint32_t>(static_cast<uint32_t>(value) << 2) |
| #define G_USB_BD_PID(bd) static_cast<UsbPid>(((bd) >> 2) & UINT32_C(0xF)) |
| |
| #define G_USB_STAT_ENDP(stat) (((stat) >> 4) & UINT32_C(0xF)) |
| #define M_USB_STAT_TX UINT32_C(1 << 3) |
| #define M_USB_STAT_ODD UINT32_C(1 << 2) |
| |
| // The various types of descriptors defined in the standard for retrieval via |
| // GetDescriptor. |
| static constexpr uint8_t kUsbDescriptorTypeMin = 1; |
| static constexpr uint8_t kUsbDescriptorTypeMax = 11; |
| enum class UsbDescriptorType : uint8_t { |
| kDevice = 1, |
| kConfiguration = 2, |
| kString = 3, |
| kInterface = 4, |
| kEndpoint = 5, |
| kDeviceQualifier = 6, |
| kOtherSpeedConfiguration = 7, |
| kInterfacePower = 8, |
| kOtg = 9, |
| kDebug = 10, |
| kInterfaceAssociation = 11, |
| }; |
| |
| // The class-specific descriptor types. |
| enum class UsbClassDescriptorType : uint8_t { |
| kDevice = 0x21, |
| kConfiguration = 0x22, |
| kString = 0x23, |
| kInterface = 0x24, |
| kEndpoint = 0x25, |
| |
| kHidHid = 0x21, |
| kHidReport = 0x22, |
| kHidPhysical = 0x23, |
| }; |
| |
| // The names of the setup request types from the standard. |
| enum class SetupRequestType { |
| kStandard = 0, |
| kClass = 1, |
| kVendor = 2, |
| kReserved = 3, |
| }; |
| |
| // Set means device-to-host, clear means host-to-device. |
| #define M_SETUP_REQUEST_TYPE_IN UINT8_C(1 << 7) |
| #define G_SETUP_REQUEST_TYPE_TYPE(type) \ |
| static_cast<SetupRequestType>(((type) >> 5) & UINT8_C(3)) |
| #define G_SETUP_REQUEST_TYPE_RECIPIENT(type) ((type) & UINT8_C(0x1F)) |
| #define G_SETUP_REQUEST_INDEX_ENDPOINT(index) ((index) & UINT8_C(0x7F)) |
| |
| // The names of the standard recipients for setup requests. |
| namespace standard_setup_recipients { |
| constexpr int kDevice = 0; |
| constexpr int kInterface = 1; |
| constexpr int kEndpoint = 2; |
| constexpr int kOther = 3; |
| } // namespace standard_setup_recipients |
| |
| namespace microsoft_feature_descriptors { |
| constexpr int kExtendedCompatibilityId = 4; |
| constexpr int kExtendedProperties = 5; |
| } // namespace microsoft_feature_descriptors |
| |
| // The HID class specification says this. Can't find any mention in the main |
| // standard. |
| #define G_DESCRIPTOR_TYPE_TYPE(descriptor_type) \ |
| ((descriptor_type) >> 5 & UINT8_C(3)) |
| namespace standard_descriptor_type_types { |
| constexpr int kStandard = 0; |
| constexpr int kClass = 1; |
| constexpr int kVendor = 2; |
| } // namespace standard_descriptor_type_types |
| |
| constexpr uint8_t vendor_specific_class() { return 0xFF; } |
| |
| class UsbFunction; |
| |
| // Allows building up a list of descriptors. This supports a much nicer API than |
| // the usual "hard-code a char[] with all the sizes and offsets at compile |
| // time". Space for each descriptor is reserved, and then it may be filled out |
| // from beginning to end at any time. |
| // |
| // An instance is the thing that the GetDescriptor operation sends to the host. |
| // This is not the concept that the core and class standards call "Foo |
| // Descriptor" etc; see Descriptor for that. |
| class UsbDescriptorList { |
| public: |
| // Represents a single descriptor. All of the contents must be written before |
| // this object is destroyed. |
| // |
| // Create one via UsbDescriptorList::CreateDescriptor. |
| class Descriptor { |
| public: |
| // All of the allocated space must be filled first. |
| ~Descriptor() { |
| if (descriptor_list_ == nullptr) { |
| return; |
| } |
| // Verify we wrote all the bytes first. |
| assert(next_index_ == end_index_); |
| --descriptor_list_->open_descriptors_; |
| } |
| |
| void AddUint16(uint16_t value) { |
| AddByte(value & 0xFF); |
| AddByte((value >> 8) & 0xFF); |
| } |
| |
| void AddByte(uint8_t value) { |
| assert(next_index_ < end_index_); |
| data()[next_index_] = value; |
| ++next_index_; |
| } |
| |
| // Overwrites an already-written byte. |
| void SetByte(int index, uint8_t value) { |
| assert(index + start_index_ < end_index_); |
| data()[index + start_index_] = value; |
| } |
| |
| private: |
| Descriptor(UsbDescriptorList *descriptor_list, int start_index, |
| int end_index) |
| : descriptor_list_(descriptor_list), |
| start_index_(start_index), |
| end_index_(end_index), |
| next_index_(start_index_) {} |
| |
| char *data() const { return &descriptor_list_->data_[0]; } |
| |
| UsbDescriptorList *const descriptor_list_; |
| const int start_index_, end_index_; |
| int next_index_; |
| |
| friend class UsbDescriptorList; |
| |
| DISALLOW_COPY_AND_ASSIGN(Descriptor); |
| }; |
| |
| UsbDescriptorList() = default; |
| ~UsbDescriptorList() = default; |
| |
| // Creates a new descriptor at the end of the list. |
| // length is the number of bytes, including the length byte. |
| // descriptor_type is the descriptor type, which is the second byte after the |
| // length. |
| ::std::unique_ptr<Descriptor> CreateDescriptor( |
| uint8_t length, UsbDescriptorType descriptor_type) { |
| return CreateDescriptor(length, static_cast<uint8_t>(descriptor_type)); |
| } |
| |
| ::std::unique_ptr<Descriptor> CreateDescriptor( |
| uint8_t length, UsbClassDescriptorType descriptor_type) { |
| assert(data_.size() > 0); |
| return CreateDescriptor(length, static_cast<uint8_t>(descriptor_type)); |
| } |
| |
| void AddPremadeDescriptor(const uint8_t *data, int length) { |
| const int start_index = data_.size(); |
| const int end_index = start_index + length; |
| data_.resize(end_index); |
| memcpy(&data_[start_index], data, length); |
| } |
| |
| void AddPremadeDescriptor(const UsbDescriptorList &other_list) { |
| other_list.CheckFinished(); |
| AddPremadeDescriptor( |
| reinterpret_cast<const uint8_t *>(other_list.data_.data()), |
| other_list.data_.size()); |
| } |
| |
| void CheckFinished() const { assert(open_descriptors_ == 0); } |
| |
| int CurrentSize() const { return data_.size(); } |
| |
| const char *GetData() const { |
| CheckFinished(); |
| return data_.data(); |
| } |
| |
| private: |
| ::std::unique_ptr<Descriptor> CreateDescriptor(uint8_t length, |
| uint8_t descriptor_type) { |
| const int start_index = data_.size(); |
| const int end_index = start_index + length; |
| data_.resize(end_index); |
| ++open_descriptors_; |
| auto r = ::std::unique_ptr<Descriptor>( |
| new Descriptor(this, start_index, end_index)); |
| r->AddByte(length); // bLength |
| r->AddByte(descriptor_type); // bDescriptorType |
| return r; |
| } |
| |
| int open_descriptors_ = 0; |
| |
| ::std::string data_; |
| |
| friend class UsbDevice; |
| |
| DISALLOW_COPY_AND_ASSIGN(UsbDescriptorList); |
| }; |
| |
| extern "C" void usb_isr(void); |
| |
| // USB state events are managed by asking each function if it wants to handle |
| // them, sequentially. For the small number of functions which can be |
| // practically supported with the limited number of endpoints, this performs |
| // better than fancier things like hash maps. |
| |
| // Manages one of the Teensy's USB peripherals as a USB slave device. |
| // |
| // This supports being a composite device with multiple functions. |
| // |
| // Attaching functions etc is called "setup", and must be completed before |
| // Initialize() is called. |
| // |
| // Detaching functions is called "teardown" and must happen after Shutdown(). |
| // TODO(Brian): Implement Shutdown(). |
| class UsbDevice final { |
| public: |
| // Represents the data that comes with a UsbPid::kSetup. |
| // Note that the order etc is important because we memcpy into this. |
| struct SetupPacket { |
| uint8_t request_type; // bmRequestType |
| uint8_t request; // bRequest |
| uint16_t value; // wValue |
| uint16_t index; // wIndex |
| uint16_t length; // wLength |
| } __attribute__((aligned(4))); |
| static_assert(sizeof(SetupPacket) == 8, "wrong size"); |
| |
| enum class SetupResponse { |
| // Indicates this function doesn't recognize the setup packet. |
| kIgnored, |
| |
| // Indicates the endpoint should be stalled. |
| // |
| // Don't return this if the packet is for another function. |
| kStall, |
| |
| // Indicates this setup packet was handled. Functions must avoid eating |
| // packets intended for other functions. |
| kHandled, |
| }; |
| |
| static constexpr int kEndpoint0MaxSize = 64; |
| |
| // The only language code we support. |
| static constexpr uint16_t english_us_code() { return 0x0409; } |
| |
| UsbDevice(int index, uint16_t vendor_id, uint16_t product_id); |
| ~UsbDevice(); |
| |
| // Ends setup and starts being an actual USB device. |
| void Initialize(); |
| |
| // Adds a string to the table and returns its index. |
| // |
| // For simplicity, we only support strings with english_us_code(). |
| // |
| // May only be called during setup. |
| int AddString(const ::std::string &string) { |
| assert(!is_set_up_); |
| const int r = strings_.size(); |
| strings_.emplace_back(string.size() * 2 + 2, '\0'); |
| strings_.back()[0] = 2 + string.size() * 2; |
| strings_.back()[1] = static_cast<uint8_t>(UsbDescriptorType::kString); |
| for (size_t i = 0; i < string.size(); ++i) { |
| strings_.back()[i * 2 + 2] = string[i]; |
| } |
| return r; |
| } |
| |
| // Sets the manufacturer string. |
| // |
| // May only be called during setup. |
| void SetManufacturer(const ::std::string &string) { |
| device_descriptor_->SetByte(14, AddString(string)); // iManufacturer |
| } |
| |
| // Sets the product string. |
| // |
| // May only be called during setup. |
| void SetProduct(const ::std::string &string) { |
| device_descriptor_->SetByte(15, AddString(string)); // iProduct |
| } |
| |
| // Sets the serial number string. |
| // |
| // May only be called during setup. |
| void SetSerialNumber(const ::std::string &string) { |
| device_descriptor_->SetByte(16, AddString(string)); // iSerialNumber |
| } |
| |
| // Queues up an empty IN packet for endpoint 0. This is a common way to |
| // respond to various kinds of configuration commands. |
| // |
| // This may only be called from the appropriate function callbacks. |
| void SendEmptyEndpoint0Packet(); |
| |
| // Queues some data to send on endpoint 0. This includes putting the initial |
| // packets into the TX buffers. |
| // |
| // This may only be called from the appropriate function callbacks. |
| void QueueEndpoint0Data(const char *data, int size); |
| |
| // Stalls an endpoint until it's cleared. |
| // |
| // This should only be called by or on behalf of the function which owns |
| // endpoint. |
| void StallEndpoint(int endpoint); |
| |
| // Configures an endpoint to send and/or receive, with or without DATA0/DATA1 |
| // handshaking. handshake should probably be true for everything except |
| // isochronous endpoints. |
| // |
| // This should only be called by or on behalf of the function which owns |
| // endpoint. |
| void ConfigureEndpointFor(int endpoint, bool rx, bool tx, bool handshake); |
| |
| void SetBdtEntry(int endpoint, Direction direction, EvenOdd odd, |
| BdtEntry bdt_entry); |
| |
| private: |
| // Clears all pending interrupts. |
| void ClearInterrupts(); |
| |
| // Deals with an interrupt that has occured. |
| void HandleInterrupt(); |
| |
| // Processes a token on endpoint 0. |
| void HandleEndpoint0Token(uint8_t stat); |
| |
| // Processes a setup packet on endpoint 0. |
| void HandleEndpoint0SetupPacket(const SetupPacket &setup_packet); |
| |
| // Sets endpoint 0 to return STALL tokens. We clear this condition upon |
| // receiving the next SETUP token. |
| void StallEndpoint0(); |
| |
| // Places the first packet from {endpoint0_data_, endpoint0_data_left_} into |
| // the TX buffers (if there is any data). This may only be called when the |
| // next TX buffer is empty. |
| bool BufferEndpoint0TxPacket(); |
| |
| // Which USB peripheral this is. |
| const int index_; |
| |
| // The string descriptors in order. |
| ::std::vector<::std::string> strings_; |
| |
| // TODO(Brian): Refactor into something more generic, because I think this is |
| // shared with all non-isochronous endpoints? |
| Data01 endpoint0_tx_toggle_; |
| EvenOdd endpoint0_tx_odd_; |
| uint8_t endpoint0_receive_buffer_[2][kEndpoint0MaxSize] |
| __attribute__((aligned(4))); |
| |
| // A temporary buffer for holding data to transmit on endpoint 0. Sometimes |
| // this is used and sometimes the data is sent directly from some other |
| // location (like for descriptors). |
| char endpoint0_transmit_buffer_[kEndpoint0MaxSize]; |
| |
| // The data we're waiting to send from endpoint 0. The data must remain |
| // constant until this transmission is done. |
| // |
| // When overwriting this, we ignore if it's already non-nullptr. The host is |
| // supposed to read all of the data before asking for more. If it doesn't do |
| // that, it will just get garbage data because it's unclear what it expects. |
| // |
| // Do note that endpoint0_data_ != nullptr && endpoint0_data_left_ == 0 is an |
| // important state. This means we're going to return a 0-length packet the |
| // next time the host asks. However, depending on the length it asked for, |
| // that might never happen. |
| const char *endpoint0_data_ = nullptr; |
| int endpoint0_data_left_ = 0; |
| |
| // If non-0, the new address we're going to start using once the status stage |
| // of the current setup request is finished. |
| uint16_t new_address_ = 0; |
| |
| UsbDescriptorList device_descriptor_list_; |
| UsbDescriptorList config_descriptor_list_; |
| |
| ::std::unique_ptr<UsbDescriptorList::Descriptor> device_descriptor_, |
| config_descriptor_; |
| |
| int configuration_ = 0; |
| |
| bool is_set_up_ = false; |
| |
| // The function which owns each endpoint. |
| ::std::vector<UsbFunction *> endpoint_mapping_; |
| // The function which owns each interface. |
| ::std::vector<UsbFunction *> interface_mapping_; |
| // All of the functions (without duplicates). |
| ::std::vector<UsbFunction *> functions_; |
| |
| // Filled out during Initialize(). |
| ::std::string microsoft_extended_id_descriptor_; |
| |
| friend void usb_isr(void); |
| friend class UsbFunction; |
| }; |
| |
| // Represents a USB function. This consists of a set of descriptors and |
| // interfaces. |
| // |
| // Each instance is a single function, so there can be multiple instances of the |
| // same subclass in the same devices (ie two serial ports). |
| class UsbFunction { |
| public: |
| UsbFunction(UsbDevice *device) : device_(device) { |
| device_->functions_.push_back(this); |
| } |
| virtual ~UsbFunction() = default; |
| |
| protected: |
| using SetupResponse = UsbDevice::SetupResponse; |
| |
| static constexpr uint8_t iad_descriptor_length() { return 8; } |
| static constexpr uint8_t interface_descriptor_length() { return 9; } |
| static constexpr uint8_t endpoint_descriptor_length() { return 7; } |
| |
| static constexpr uint8_t m_endpoint_address_in() { return 1 << 7; } |
| static constexpr uint8_t m_endpoint_attributes_control() { return 0x00; } |
| static constexpr uint8_t m_endpoint_attributes_isochronous() { return 0x01; } |
| static constexpr uint8_t m_endpoint_attributes_bulk() { return 0x02; } |
| static constexpr uint8_t m_endpoint_attributes_interrupt() { return 0x03; } |
| |
| // Adds a new endpoint and returns its index. |
| // |
| // Note that at least one descriptor for this newly created endpoint must be |
| // added via CreateConfigDescriptor. |
| // |
| // TODO(Brian): Does this hardware actually only support a single direction |
| // per endpoint number, or can it get a total of 30 endpoints max? |
| // |
| // May only be called during setup. |
| int AddEndpoint(); |
| |
| // Adds a new interface and returns its index. |
| // |
| // You'll probably want to put this new interface in at least one descriptor |
| // added via CreateConfigDescriptor. |
| // |
| // May only be called during setup. |
| int AddInterface(); |
| |
| // Adds a new descriptor in the configuration descriptor list. See |
| // UsbDescriptorList::CreateDescriptor for details. |
| // |
| // Note that the order of calls to this is highly significant. In general, |
| // this should only be called from Initialize(). |
| // |
| // May only be called during setup. |
| template <typename T> |
| ::std::unique_ptr<UsbDescriptorList::Descriptor> CreateDescriptor( |
| uint8_t length, T descriptor_type) { |
| return device_->config_descriptor_list_.CreateDescriptor(length, |
| descriptor_type); |
| } |
| void AddPremadeDescriptor(const uint8_t *data, int length) { |
| device_->config_descriptor_list_.AddPremadeDescriptor(data, length); |
| } |
| void AddPremadeDescriptor(const UsbDescriptorList &other_list) { |
| device_->config_descriptor_list_.AddPremadeDescriptor(other_list); |
| } |
| |
| UsbDevice *device() const { return device_; } |
| |
| void CreateIadDescriptor(int first_interface, int interface_count, |
| int function_class, int function_subclass, |
| int function_protocol, |
| const ::std::string &function); |
| |
| // Sets the interface GUIDs for this function. Each GUID (one per interface?) |
| // should be followed by a NUL. |
| // |
| // If this is never called, no GUID extended property will be reported. |
| // |
| // This is needed to pass to Windows so WinUSB will be happy. Generate them at |
| // https://www.guidgenerator.com/online-guid-generator.aspx (Uppcase, Braces, |
| // and Hyphens). |
| // |
| // May only be called during setup. |
| void SetMicrosoftDeviceInterfaceGuids(const ::std::string &guids); |
| |
| private: |
| virtual void Initialize() = 0; |
| |
| virtual SetupResponse HandleEndpoint0SetupPacket( |
| const UsbDevice::SetupPacket & /*setup_packet*/) { |
| return SetupResponse::kIgnored; |
| } |
| |
| virtual SetupResponse HandleEndpoint0OutPacket(void * /*data*/, |
| int /*data_length*/) { |
| return SetupResponse::kIgnored; |
| } |
| |
| virtual SetupResponse HandleGetDescriptor( |
| const UsbDevice::SetupPacket & /*setup_packet*/) { |
| return SetupResponse::kIgnored; |
| } |
| |
| // Returns the concatenated compatible ID and subcompatible ID. |
| virtual ::std::string MicrosoftExtendedCompatibleId() { |
| // Default to both of them being "unused". |
| return ::std::string(16, '\0'); |
| } |
| |
| virtual void HandleOutFinished(int /*endpoint*/, BdtEntry * /*bdt_entry*/) {} |
| virtual void HandleInFinished(int /*endpoint*/, BdtEntry * /*bdt_entry*/, |
| EvenOdd /*odd*/) {} |
| |
| // Called when a given interface is configured (aka "experiences a |
| // configuration event"). This means all rx and tx buffers have been cleared |
| // and should be filled as appropriate, starting from data0. Also, |
| // ConfigureEndpointFor should be called with the appropriate arguments. |
| virtual void HandleConfigured(int endpoint) = 0; |
| |
| // Should reset everything to use the even buffers next. |
| virtual void HandleReset() = 0; |
| |
| int first_interface_ = -1; |
| |
| // Filled out during Initialize(). |
| ::std::string microsoft_extended_property_descriptor_; |
| |
| UsbDevice *const device_; |
| |
| friend class UsbDevice; |
| }; |
| |
| } // namespace frc971::teensy |
| |
| #endif // MOTORS_USB_USB_H_ |