| // 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 UsbModemInfo; |
| 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(); |
| |
| // 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(); |
| |
| // 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 the special USB message(s) is sent to the mass storage |
| // endpoint by SendMessageToMassStorageEndpoint(). |
| 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_; |
| |
| DISALLOW_COPY_AND_ASSIGN(UsbModemSwitchOperation); |
| }; |
| |
| } // namespace mist |
| |
| #endif // MIST_USB_MODEM_SWITCH_OPERATION_H_ |