chromium/services/device/usb/usb_device_handle_win.h

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef SERVICES_DEVICE_USB_USB_DEVICE_HANDLE_WIN_H_
#define SERVICES_DEVICE_USB_USB_DEVICE_HANDLE_WIN_H_

#include <list>
#include <map>
#include <memory>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/win/scoped_handle.h"
#include "services/device/usb/scoped_winusb_handle.h"
#include "services/device/usb/usb_device_handle.h"

struct _USB_NODE_CONNECTION_INFORMATION_EX;

namespace base {
class RefCountedBytes;
class SequencedTaskRunner;
}  // namespace base

namespace device {

class UsbDeviceWin;

// UsbDeviceHandle class provides basic I/O related functionalities.
class UsbDeviceHandleWin : public UsbDeviceHandle {
 public:
  UsbDeviceHandleWin(const UsbDeviceHandleWin&) = delete;
  UsbDeviceHandleWin& operator=(const UsbDeviceHandleWin&) = delete;

  scoped_refptr<UsbDevice> GetDevice() const override;
  void Close() override;
  void SetConfiguration(int configuration_value,
                        ResultCallback callback) override;
  void ClaimInterface(int interface_number, ResultCallback callback) override;
  void ReleaseInterface(int interface_number, ResultCallback callback) override;
  void SetInterfaceAlternateSetting(int interface_number,
                                    int alternate_setting,
                                    ResultCallback callback) override;
  void ResetDevice(ResultCallback callback) override;
  void ClearHalt(mojom::UsbTransferDirection direction,
                 uint8_t endpoint_number,
                 ResultCallback callback) override;

  void ControlTransfer(mojom::UsbTransferDirection direction,
                       mojom::UsbControlTransferType request_type,
                       mojom::UsbControlTransferRecipient recipient,
                       uint8_t request,
                       uint16_t value,
                       uint16_t index,
                       scoped_refptr<base::RefCountedBytes> buffer,
                       unsigned int timeout,
                       TransferCallback callback) override;

  void IsochronousTransferIn(uint8_t endpoint,
                             const std::vector<uint32_t>& packet_lengths,
                             unsigned int timeout,
                             IsochronousTransferCallback callback) override;

  void IsochronousTransferOut(uint8_t endpoint,
                              scoped_refptr<base::RefCountedBytes> buffer,
                              const std::vector<uint32_t>& packet_lengths,
                              unsigned int timeout,
                              IsochronousTransferCallback callback) override;

  void GenericTransfer(mojom::UsbTransferDirection direction,
                       uint8_t endpoint_number,
                       scoped_refptr<base::RefCountedBytes> buffer,
                       unsigned int timeout,
                       TransferCallback callback) override;
  const mojom::UsbInterfaceInfo* FindInterfaceByEndpoint(
      uint8_t endpoint_address) override;

 protected:
  friend class UsbDeviceWin;

  // Constructor used to build a connection to the device.
  UsbDeviceHandleWin(scoped_refptr<UsbDeviceWin> device);

  // Constructor used to build a connection to the device's parent hub. To avoid
  // bugs in USB hub drivers a single global sequenced task runner is used for
  // all calls to the driver.
  UsbDeviceHandleWin(
      scoped_refptr<UsbDeviceWin> device,
      base::win::ScopedHandle handle,
      scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);

  ~UsbDeviceHandleWin() override;

  void UpdateFunction(int interface_number,
                      const std::wstring& function_driver,
                      const std::wstring& function_path);

 private:
  struct Interface;
  class Request;

  using OpenInterfaceCallback = base::OnceCallback<void(Interface*)>;

  struct Interface {
    Interface();

    Interface(const Interface&) = delete;
    Interface& operator=(const Interface&) = delete;

    ~Interface();

    // This may be nullptr in the rare case of a device which doesn't have any
    // interfaces. In that case the Windows API still considers the device to
    // have a single function which is represented here by initializing
    // |interface_number| and |first_interface| to create a fake interface 0.
    raw_ptr<const mojom::UsbInterfaceInfo> info = nullptr;

    // These fields are copied from |info| and initialized to 0 in case it is
    // nullptr.
    uint8_t interface_number = 0;
    uint8_t first_interface = 0;

    // In a composite device each function has its own driver and path to open.
    std::wstring function_driver;
    std::wstring function_path;
    base::win::ScopedHandle function_handle;

    ScopedWinUsbHandle handle;
    bool claimed = false;

    // The currently selected alternative interface setting. This is assumed to
    // be the first alternate when the device is opened.
    uint8_t alternate_setting = 0;

    // The count of outstanding requests, including associated interfaces
    // claimed which require keeping the handles owned by this object open.
    int reference_count = 0;

    // Closures to execute when |function_path| has been populated.
    std::vector<OpenInterfaceCallback> ready_callbacks;
  };

  struct Endpoint {
    raw_ptr<const mojom::UsbInterfaceInfo, DanglingUntriaged> interface;
    mojom::UsbTransferType type;
  };

  void OpenInterfaceHandle(Interface* interface,
                           OpenInterfaceCallback callback);

  // Interfaces on a USB device are organized into "functions". When making a
  // request to a device the first interface of each function is the one that
  // has a valid |function_handle|. This function finds the correct interface
  // for making requests to the provided interface based on the device's driver
  // configuration.
  Interface* GetFirstInterfaceForFunction(Interface* interface);
  void OnFunctionAvailable(OpenInterfaceCallback callback,
                           Interface* interface);
  void OnFirstInterfaceOpened(int interface_number,
                              OpenInterfaceCallback callback,
                              Interface* first_interface);
  void OnInterfaceClaimed(ResultCallback callback, Interface* interface);
  void OnSetAlternateInterfaceSetting(int interface_number,
                                      int alternate_setting,
                                      ResultCallback callback,
                                      bool result);
  void RegisterEndpoints(const mojom::UsbInterfaceInfo* interface,
                         const mojom::UsbAlternateInterfaceInfo& alternate);
  void UnregisterEndpoints(const mojom::UsbAlternateInterfaceInfo& alternate);
  void OnClearHalt(int interface_number, ResultCallback callback, bool result);
  void OpenInterfaceForControlTransfer(
      mojom::UsbControlTransferRecipient recipient,
      uint16_t index,
      OpenInterfaceCallback callback);
  void OnFunctionAvailableForEp0(OpenInterfaceCallback callback,
                                 Interface* interface);
  void OnInterfaceOpenedForControlTransfer(
      mojom::UsbTransferDirection direction,
      mojom::UsbControlTransferType request_type,
      mojom::UsbControlTransferRecipient recipient,
      uint8_t request,
      uint16_t value,
      uint16_t index,
      scoped_refptr<base::RefCountedBytes> buffer,
      unsigned int timeout,
      TransferCallback callback,
      Interface* interface);
  Request* MakeRequest(Interface* interface);
  std::unique_ptr<Request> UnlinkRequest(Request* request);
  void GotNodeConnectionInformation(
      TransferCallback callback,
      std::unique_ptr<_USB_NODE_CONNECTION_INFORMATION_EX> node_connection_info,
      scoped_refptr<base::RefCountedBytes> buffer,
      std::pair<DWORD, DWORD> result_and_bytes_transferred);
  void GotDescriptorFromNodeConnection(
      TransferCallback callback,
      scoped_refptr<base::RefCountedBytes> request_buffer,
      scoped_refptr<base::RefCountedBytes> original_buffer,
      std::pair<DWORD, DWORD> result_and_bytes_transferred);
  void TransferComplete(TransferCallback callback,
                        scoped_refptr<base::RefCountedBytes> buffer,
                        Request* request_ptr,
                        DWORD win32_result,
                        size_t bytes_transferred);
  void ReportIsochronousError(const std::vector<uint32_t>& packet_lengths,
                              IsochronousTransferCallback callback,
                              mojom::UsbTransferStatus status);
  bool AllFunctionsEnumerated() const;
  void ReleaseInterfaceReference(Interface* interface);

  SEQUENCE_CHECKER(sequence_checker_);

  scoped_refptr<UsbDeviceWin> device_;

  // |hub_handle_| or all the handles for claimed interfaces in |interfaces_|
  // must outlive their associated |requests_| because individual Request
  // objects hold on to the raw handles for the purpose of calling
  // GetOverlappedResult().
  base::win::ScopedHandle hub_handle_;

  std::map<uint8_t, Interface> interfaces_;
  std::map<uint8_t, Endpoint> endpoints_;
  std::list<std::unique_ptr<Request>> requests_;

  // Control transfers which are waiting for a function handle to be ready.
  std::vector<OpenInterfaceCallback> ep0_ready_callbacks_;

  scoped_refptr<base::SequencedTaskRunner> task_runner_;
  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;

  base::WeakPtrFactory<UsbDeviceHandleWin> weak_factory_{this};
};

}  // namespace device

#endif  // SERVICES_DEVICE_USB_USB_DEVICE_HANDLE_WIN_H_