blob: 4ebcf157b98a09181ab0da647088e82ae736fc87 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS 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 MIST_USB_MODEM_SWITCH_OPERATION_H_
#define MIST_USB_MODEM_SWITCH_OPERATION_H_
#include <stdint.h>
#include <memory>
#include <string>
#include <base/callback.h>
#include <base/cancelable_callback.h>
#include <base/macros.h>
#include <base/memory/weak_ptr.h>
#include <base/time/time.h>
#include "mist/usb_device_event_observer.h"
namespace mist {
class Context;
class UsbBulkTransfer;
class UsbDevice;
class UsbModemSwitchContext;
class UsbTransfer;
// A USB modem switch operation for switching a USB modem into the modem mode.
// The whole operation involves the following tasks:
// 1. Open the USB modem device. If the modem has a USB configuration that
// exposes a MBIM interface, select that configuration and complete the
// switch operation. Otherwise, find and claim the mass storage interface of
// the mdoem.
// 2. Initiate a bulk output transfer of a (or multiple) special USB message(s)
// to the mass storage endpoint of the modem.
// 3. On some modems, a bulk input transfer from the mass storage endpoint of
// the modem is expected after completing each bulk output transfer.
// 4. Once the transfer of the last message completes, the modem is expected to
// disconnect from the USB bus and then reconnect to the bus after it has
// been switched to the modem mode.
//
// As mist may run multiple modem switch operations concurrently, in order to
// maximize the overall concurrency, the modem switch operation is broken up
// into the aforementioned tasks and each task is scheduled to execute in the
// message loop via EventDispatcher.
class UsbModemSwitchOperation
: public base::SupportsWeakPtr<UsbModemSwitchOperation>,
public UsbDeviceEventObserver {
public:
using CompletionCallback =
base::Callback<void(UsbModemSwitchOperation* operation, bool success)>;
// Constructs a UsbModemSwitchOperation object by taking a raw pointer to a
// Context object as |context| and a raw pointer to a UsbModemSwitchContext
// object as |switch_context| that contains information about the device to
// be switched to the modem mode. The ownership of |context| is not
// transferred, and thus it should outlive this object. The ownership of
// |switch_context| is transferred.
UsbModemSwitchOperation(Context* context,
UsbModemSwitchContext* switch_context);
UsbModemSwitchOperation(const UsbModemSwitchOperation&) = delete;
UsbModemSwitchOperation& operator=(const UsbModemSwitchOperation&) = delete;
~UsbModemSwitchOperation();
// Starts the modem switch operation. Upon the completion of the operation,
// the completion callback |completion_callback| is invoked with the status
// of the operation.
void Start(const CompletionCallback& completion_callback);
// Cancels the modem switch operation and closes any open device. It is a
// no-op if the operation has not been started by Start().
void Cancel();
private:
using Task = void (UsbModemSwitchOperation::*)();
using UsbTransferCompletionHandler =
void (UsbModemSwitchOperation::*)(UsbTransfer* transfer);
// Schedules the specified |task| in the message loop for execution. At most
// one pending task is allowed, so any pending task previously scheduled by
// ScheduleTask() or ScheduleDelayedTask() is cancelled before |task| is
// scheduled.
void ScheduleTask(Task task);
// Schedules the specified |task| in the message loop for execution after the
// specified |delay|. At most one pending task is allowed, so any pending
// task previously scheduled by ScheduleTask() or ScheduleDelayedTask() is
// cancelled before |task| is scheduled.
void ScheduleDelayedTask(Task task, const base::TimeDelta& delay);
// Completes the operation, which invokes the completion callback with the
// status of the operation as |success|. The completion callback may delete
// this object, so this object should not be accessed after this method
// returns.
void Complete(bool success);
// Detaches all the kernel drivers associated with the interfaces of the
// currently active USB configuration. Continues to detach other kernel
// drivers if it fails to detach any driver.
void DetachAllKernelDrivers();
// Returns the value of the USB configuration at which the device exposes a
// MBIM interface, or kUsbConfigurationValueInvalid if no MBIM interface is
// found.
int GetMBIMConfigurationValue();
// Sets the USB configuration of the device to |configuration|. Returns true
// on success.
bool SetConfiguration(int configuration);
// Closes the device.
void CloseDevice();
// Opens the device. If the device has a USB configuration that exposes a MBIM
// interface, selects that configuration and completes the switch operation.
// Otherwise, finds and claims the mass storage interface on the device.
void OpenDeviceAndSelectInterface();
// Clears the halt condition on the endpoint at |endpoint_address|. Returns
// true on success.
bool ClearHalt(uint8_t endpoint_address);
// Sends a special USB message to the mass storage endpoint of the device.
void SendMessageToMassStorageEndpoint();
// Receives a USB message from the mass storage endpoint of the device.
void ReceiveMessageFromMassStorageEndpoint();
// Creates and submits a USB bulk transfer to the specified |endpoint_address|
// on the device. |length| specifies the size of the transfer in bytes. For a
// host-to-device transfer, |data| should point to a buffer containing
// |length| bytes of data to be transferred. For a device-to-host transfer,
// |data| is not used and thus ignored. |completion_handler| will be invoked
// upon the completion of the transfer.
void InitiateUsbBulkTransfer(uint8_t endpoint_address,
const uint8_t* data,
int length,
UsbTransferCompletionHandler completion_handler);
// Schedules the invocation of SendMessageToMassStorageEndpoint() on the next
// USB message to be sent to the mass storage endpoint of the device, or when
// there is no more message to send, schedule the wait for the device to
// reconnect.
void ScheduleNextMessageToMassStorageEndpoint();
// Starts waiting for the device to reconnect to the USB bus.
void StartWaitingForDeviceToReconnect();
// Invoked upon the completion of the last USB bulk transfer submitted by
// SendMessageToMassStorageEndpoint().
void OnSendMessageCompleted(UsbTransfer* transfer);
// Invoked upon the completion of the last USB bulk transfer submitted by
// ReceiveMessageFromMassStorageEndpoint().
void OnReceiveMessageCompleted(UsbTransfer* transfer);
// Invoked when this switcher times out waiting for the device to reconnect
// to the bus, after a specified period time since
// StartWaitingForDeviceToReconnect() is invoked.
void OnReconnectTimeout();
// Implements UsbDeviceEventObserver.
void OnUsbDeviceAdded(const std::string& sys_path,
uint8_t bus_number,
uint8_t device_address,
uint16_t vendor_id,
uint16_t product_id) override;
void OnUsbDeviceRemoved(const std::string& sys_path) override;
Context* const context_;
std::unique_ptr<UsbModemSwitchContext> switch_context_;
std::unique_ptr<UsbDevice> device_;
CompletionCallback completion_callback_;
bool interface_claimed_;
uint8_t interface_number_;
uint8_t in_endpoint_address_;
uint8_t out_endpoint_address_;
int message_index_;
int num_usb_messages_;
std::unique_ptr<UsbBulkTransfer> bulk_transfer_;
base::CancelableClosure pending_task_;
base::CancelableClosure reconnect_timeout_callback_;
};
} // namespace mist
#endif // MIST_USB_MODEM_SWITCH_OPERATION_H_