blob: 7b12d8c8d114ca301b9cda1f4fa3ab39162ed589 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVICES_USB_LIB_USB_INCLUDE_USB_USB_H_
#define SRC_DEVICES_USB_LIB_USB_INCLUDE_USB_USB_H_
#include <fuchsia/hardware/usb/c/banjo.h>
#include <fuchsia/hardware/usb/composite/c/banjo.h>
#include <zircon/assert.h>
#include <zircon/compiler.h>
#include <zircon/hw/usb.h>
#ifdef __cplusplus
#include <fuchsia/hardware/usb/cpp/banjo.h>
#include <optional>
#endif
__BEGIN_CDECLS
// helper function for claiming additional interfaces that satisfy the want_interface predicate,
// want_interface will be passed the supplied arg
// clang-format off
zx_status_t usb_claim_additional_interfaces(
usb_composite_protocol_t* comp,
bool (*want_interface)(usb_interface_descriptor_t*, void*),
void* arg);
// clang-format on
// Utilities for iterating through descriptors within a device's USB configuration descriptor
typedef struct {
uint8_t* desc; // start of configuration descriptor
uint8_t* desc_end; // end of configuration descriptor
uint8_t* current; // current position in configuration descriptor
} usb_desc_iter_t;
// initializes a usb_desc_iter_t
zx_status_t usb_desc_iter_init(usb_protocol_t* usb, usb_desc_iter_t* iter);
// Clones a usb_desc_iter_t
zx_status_t usb_desc_iter_clone(const usb_desc_iter_t* src, usb_desc_iter_t* dest);
// releases resources in a usb_desc_iter_t
void usb_desc_iter_release(usb_desc_iter_t* iter);
// resets iterator to the beginning
void usb_desc_iter_reset(usb_desc_iter_t* iter);
// returns the descriptor header structure currently pointed by the iterator. If the current
// iterator does not point to a valid descriptor header structure, NULL would be returned and user
// is expected to handle the error case and end the descriptor parsing.
usb_descriptor_header_t* usb_desc_iter_peek(usb_desc_iter_t* iter);
// increase the iterator to the next descriptor. If the current descriptor is not a valid descriptor
// header structure, returns false, otherwise, returns true. The iterator would not be increased
// if false is returned and user is expected to handle the error case and end the descriptor
// parsing.
bool usb_desc_iter_advance(usb_desc_iter_t* iter);
// returns the expected structure with structure size currently pointed by the iterator. If the
// length of descriptor buffer current pointed by the iterator is not enough to hold the structure,
// NULL would be returned, user is expected to handle the error case.
void* usb_desc_iter_get_structure(usb_desc_iter_t* iter, size_t structure_size);
// returns the next interface descriptor, optionally skipping alternate interfaces
usb_interface_descriptor_t* usb_desc_iter_next_interface(usb_desc_iter_t* iter, bool skip_alt);
// returns the next endpoint descriptor within the current interface
usb_endpoint_descriptor_t* usb_desc_iter_next_endpoint(usb_desc_iter_t* iter);
// returns the next ss-companion descriptor within the current interface
usb_ss_ep_comp_descriptor_t* usb_desc_iter_next_ss_ep_comp(usb_desc_iter_t* iter);
static inline zx_status_t usb_get_descriptor(const usb_protocol_t* usb, uint8_t request_type,
uint16_t type, uint16_t index, void* data,
size_t length, zx_time_t timeout, size_t* out_length) {
return usb_control_in(usb, request_type | USB_DIR_IN, USB_REQ_GET_DESCRIPTOR,
(uint16_t)(type << 8 | index), 0, timeout, data, length, out_length);
}
static inline zx_status_t usb_get_status(const usb_protocol_t* usb, uint8_t request_type,
uint16_t index, void* data, size_t length,
zx_time_t timeout, size_t* out_length) {
return usb_control_in(usb, request_type | USB_DIR_IN, USB_REQ_GET_STATUS, 0, index, timeout, data,
length, out_length);
}
static inline zx_status_t usb_set_feature(const usb_protocol_t* usb, uint8_t request_type,
uint16_t feature, uint16_t index, zx_time_t timeout) {
return usb_control_out(usb, request_type, USB_REQ_SET_FEATURE, feature, index, timeout, NULL, 0);
}
static inline zx_status_t usb_clear_feature(const usb_protocol_t* usb, uint8_t request_type,
uint16_t feature, uint16_t index, zx_time_t timeout) {
return usb_control_out(usb, request_type, USB_REQ_CLEAR_FEATURE, feature, index, timeout, NULL,
0);
}
__END_CDECLS
#ifdef __cplusplus
namespace usb {
typedef struct {
usb_endpoint_descriptor_t descriptor;
usb_ss_ep_comp_descriptor_t ss_companion;
// True if ss_companion is populated.
bool has_companion;
} usb_iter_endpoint_descriptor_t;
class UsbDevice : public ddk::UsbProtocolClient {
public:
UsbDevice() {}
UsbDevice(const usb_protocol_t* proto) : UsbProtocolClient(proto) {}
UsbDevice(zx_device_t* parent) : UsbProtocolClient(parent) {}
zx_status_t ClearFeature(uint8_t request_type, uint16_t feature, uint16_t index,
zx_time_t timeout) {
usb_protocol_t proto;
GetProto(&proto);
return usb_clear_feature(&proto, request_type, feature, index, timeout);
}
zx_status_t GetDescriptor(uint8_t request_type, uint16_t type, uint16_t index, void* data,
size_t length, zx_time_t timeout, size_t* out_length) {
usb_protocol_t proto;
GetProto(&proto);
return usb_get_descriptor(&proto, request_type, type, index, data, length, timeout, out_length);
}
zx_status_t GetStatus(uint8_t request_type, uint16_t index, void* data, size_t length,
zx_time_t timeout, size_t* out_length) {
usb_protocol_t proto;
GetProto(&proto);
return usb_get_status(&proto, request_type, index, data, length, timeout, out_length);
}
zx_status_t SetFeature(int8_t request_type, uint16_t feature, uint16_t index, zx_time_t timeout) {
usb_protocol_t proto;
GetProto(&proto);
return usb_set_feature(&proto, request_type, feature, index, timeout);
}
};
// DescriptorList is owned by Interface, where Interface is accessed by an InterfaceList.
// DescriptorList iterates through all child descriptors of Interface by utilizing
// Interface.GetDescriptorList().
//
// The InterfaceList will enumerate interfaces in the client, and will skip any alternate interfaces
// if skip_alt is true (see page 268 of the USB 2.0 specification for more information).
//
// Example Usage #1:
// std::optional<InterfaceList> my_list;
// status = InterfaceList::Create(my_client, true, &my_list);
// if (status != ZX_OK) {
// ...
// }
//
// for (auto& interface : *my_list) {
// for (auto& descriptor : interface.GetDescriptorList()) {
// ...
// }
// }
//
// Example Usage #2:
// std::optional<InterfaceList> my_list;
// status = InterfaceList::Create(my_client, true, &my_list);
// if (status != ZX_OK) {
// ...
// }
//
// for (auto& interface : *my_list) {
// auto dList_itr = interface.GetDescriptorList().begin(); // or cbegin().
// do {
// ...
// } while (++dList_itr != interface.GetDescriptorList().end());
// }
class DescriptorList {
private:
class iterator_impl;
public:
using iterator = iterator_impl;
using const_iterator = iterator_impl;
DescriptorList(const usb_desc_iter_t& iter, const usb_interface_descriptor_t* descriptor)
: iter_(iter), descriptor_(descriptor) {}
DescriptorList() = delete;
const usb_interface_descriptor_t* descriptor() const { return descriptor_; }
iterator begin() const;
const_iterator cbegin() const;
iterator end() const;
const_iterator cend() const;
private:
class iterator_impl {
public:
friend class DescriptorList;
iterator_impl(const usb_desc_iter_t& iter, const usb_descriptor_header_t* header)
: iter_(iter), header_(header) {}
bool operator==(const iterator_impl& other) const { return (other.header_ == header_); }
bool operator!=(const iterator_impl& other) const { return !(*this == other); }
iterator_impl operator++(int) {
iterator_impl ret(*this);
++(*this);
return ret;
}
iterator_impl& operator++() {
ReadHeader(&iter_, &header_);
return *this;
}
const usb_descriptor_header_t* header() const { return header_; }
const usb_descriptor_header_t& operator*() const { return *header_; }
const usb_descriptor_header_t* operator->() const { return header_; }
private:
// Using the given iter, read the next endpoint descriptor(s).
static void ReadHeader(usb_desc_iter_t* iter, const usb_descriptor_header_t** out);
usb_desc_iter_t iter_;
const usb_descriptor_header_t* header_;
};
usb_desc_iter_t iter_;
const usb_interface_descriptor_t* descriptor_;
};
// EndpointList is owned by Interface, where Interface is accessed by an InterfaceList. It is
// possible to enumerate all USB endpoint descriptors by utilizing Interface.getEndpointList().
class EndpointList {
private:
class iterator_impl;
public:
using iterator = iterator_impl;
using const_iterator = iterator_impl;
EndpointList(const usb_desc_iter_t& iter, const usb_interface_descriptor_t* descriptor)
: iter_(iter), descriptor_(descriptor) {}
EndpointList() = delete;
const usb_interface_descriptor_t* descriptor() const { return descriptor_; }
iterator begin() const;
const_iterator cbegin() const;
iterator end() const;
const_iterator cend() const;
private:
class iterator_impl {
public:
friend class EndpointList;
iterator_impl(const usb_desc_iter_t& iter, const usb_iter_endpoint_descriptor_t& endpoint)
: iter_(iter), endpoint_(endpoint) {}
bool operator==(const iterator_impl& other) const {
const usb_endpoint_descriptor_t* a = &endpoint_.descriptor;
const usb_endpoint_descriptor_t* b = &other.endpoint_.descriptor;
// Note that within a configuration, endpoint addresses are unique.
return (a->bEndpointAddress == b->bEndpointAddress);
}
bool operator!=(const iterator_impl& other) const { return !(*this == other); }
iterator_impl operator++(int) {
iterator_impl ret(*this);
++(*this);
return ret;
}
iterator_impl& operator++() {
endpoint_ = {};
ReadEp(&iter_, &endpoint_);
return *this;
}
const usb_iter_endpoint_descriptor_t* endpoint() const { return &endpoint_; }
const usb_iter_endpoint_descriptor_t& operator*() const { return endpoint_; }
const usb_iter_endpoint_descriptor_t* operator->() const { return &endpoint_; }
private:
// Using the given iter, read the next endpoint descriptor(s).
static void ReadEp(usb_desc_iter_t* iter, usb_iter_endpoint_descriptor_t* out);
usb_desc_iter_t iter_;
usb_iter_endpoint_descriptor_t endpoint_;
};
usb_desc_iter_t iter_;
const usb_interface_descriptor_t* descriptor_;
};
class Interface {
public:
DescriptorList GetDescriptorList() const;
EndpointList GetEndpointList() const;
const usb_interface_descriptor_t* descriptor() const { return descriptor_; }
friend class InterfaceList;
private:
Interface(const usb_desc_iter_t& iter, const usb_interface_descriptor_t* descriptor)
: descriptor_(descriptor), iter_(iter) {}
// Advances iter_ to the next usb_interface_descriptor_t.
void Next(bool skip_alt);
const usb_interface_descriptor_t* descriptor_;
usb_desc_iter_t iter_;
};
// An InterfaceList can be used for enumerating USB interfaces. It implements a
// standard C++ iterator interface, which can be used by a for loop.
class InterfaceList {
private:
class iterator_impl;
public:
using iterator = iterator_impl;
using const_iterator = iterator_impl;
InterfaceList() = delete;
InterfaceList(const usb_desc_iter_t& iter, bool skip_alt) : iter_(iter), skip_alt_(skip_alt) {}
InterfaceList(InterfaceList&&) = delete;
InterfaceList& operator=(InterfaceList&&) = delete;
~InterfaceList() {
if (iter_.desc) {
usb_desc_iter_release(&iter_);
}
}
static zx_status_t Create(const ddk::UsbProtocolClient& client, bool skip_alt,
std::optional<InterfaceList>* out);
size_t size() {
return reinterpret_cast<size_t>(iter_.desc_end) - reinterpret_cast<size_t>(iter_.desc);
}
iterator begin() const;
const_iterator cbegin() const;
iterator end() const;
const_iterator cend() const;
private:
class iterator_impl {
public:
iterator_impl(const usb_desc_iter_t& iter, bool skip_alt,
const usb_interface_descriptor_t* descriptor)
: skip_alt_(skip_alt), interface_(iter, descriptor) {}
bool operator==(const iterator_impl& other) const {
return interface_.descriptor_ == other.interface_.descriptor_;
}
bool operator!=(const iterator_impl& other) const { return !(*this == other); }
iterator_impl operator++(int) {
iterator_impl ret(*this);
++(*this);
return ret;
}
iterator_impl& operator++() {
interface_.Next(skip_alt_);
return *this;
}
const Interface* get() const { return &interface_; }
const Interface& operator*() const { return interface_; }
const Interface* operator->() const { return &interface_; }
private:
const bool skip_alt_;
Interface interface_;
};
usb_desc_iter_t iter_{};
bool skip_alt_;
};
} // namespace usb
#endif
#endif // SRC_DEVICES_USB_LIB_USB_INCLUDE_USB_USB_H_