| // Copyright 2022 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. |
| |
| #include "adb-function.h" |
| |
| #include <lib/driver/compat/cpp/compat.h> |
| #include <lib/driver/logging/cpp/structured_logger.h> |
| #include <lib/zx/vmar.h> |
| #include <zircon/assert.h> |
| |
| #include <cstdint> |
| #include <optional> |
| |
| #include <usb/peripheral.h> |
| #include <usb/request-cpp.h> |
| |
| #include "zircon/status.h" |
| |
| namespace usb_adb_function { |
| |
| namespace { |
| |
| // CompleterType follows fidl::internal::WireCompleter<RequestType>::Async |
| template <typename CompleterType> |
| void CompleteTxn(CompleterType& completer, zx_status_t status) { |
| if (status == ZX_OK) { |
| completer.Reply(fit::ok()); |
| } else { |
| completer.Reply(fit::error(status)); |
| } |
| } |
| |
| } // namespace |
| |
| void UsbAdbDevice::StartAdb(StartAdbRequestView request, StartAdbCompleter::Sync& completer) { |
| if (adb_binding_.has_value()) { |
| zxlogf(WARNING, "ADB already connected"); |
| completer.ReplyError(ZX_ERR_ALREADY_BOUND); |
| return; |
| } |
| |
| switch (state_) { |
| case State::kStoppingUsb: |
| zxlogf(WARNING, "ADB connected while stopping"); |
| completer.ReplyError(ZX_ERR_BAD_STATE); |
| return; |
| case State::kOnline: |
| // We're already online, so send the status change immediately. |
| if (auto result = |
| fidl::WireSendEvent(request->interface)->OnStatusChanged(fadb::StatusFlags::kOnline); |
| !result.ok()) { |
| zxlogf(ERROR, "Could not call UsbAdbImpl.OnStatusChanged."); |
| } |
| break; |
| case State::kAwaitingUsbConnection: |
| break; |
| } |
| zxlogf(INFO, "ADB client connected"); |
| |
| adb_binding_.emplace(fdf::Dispatcher::GetCurrent()->async_dispatcher(), |
| std::move(request->interface), this, [this](fidl::UnbindInfo info) { |
| zxlogf(INFO, "Device closed with reason '%s'", |
| info.FormatDescription().c_str()); |
| ResetOrStopUsb(); |
| }); |
| completer.ReplySuccess(); |
| } |
| |
| void UsbAdbDevice::StopAdb(StopAdbCompleter::Sync& completer) { |
| zxlogf(INFO, "ADB client requested disconnect."); |
| stop_completers_.push_back(completer.ToAsync()); |
| ResetOrStopUsb(); |
| } |
| |
| void UsbAdbDevice::ResetOrStopUsb() { |
| switch (state_) { |
| case State::kStoppingUsb: |
| zxlogf(INFO, "Stop requested, but already stopping"); |
| return; |
| case State::kOnline: |
| zxlogf(INFO, "Stopping USB"); |
| break; |
| case State::kAwaitingUsbConnection: |
| zxlogf(INFO, "Stop requested during USB startup"); |
| break; |
| } |
| |
| // Purge any requests from internal queues. |
| // TODO(b/417808660): Replace logs with Inspect once the bug is fixed. |
| zxlogf(INFO, "rx_requests: %ld", rx_requests_.size()); |
| while (!rx_requests_.empty()) { |
| rx_requests_.front().Reply(fit::error(ZX_ERR_BAD_STATE)); |
| rx_requests_.pop(); |
| } |
| // TODO(b/417808660): Replace logs with Inspect once the bug is fixed. |
| zxlogf(INFO, "pending_replies: %ld", pending_replies_.size()); |
| while (!pending_replies_.empty()) { |
| bulk_out_ep_.PutRequest( |
| usb::FidlRequest(std::move(pending_replies_.front().request().value()))); |
| pending_replies_.pop(); |
| } |
| // TODO(b/417808660): Replace logs with Inspect once the bug is fixed. |
| zxlogf(INFO, "tx_pending_reqs: %ld", tx_pending_reqs_.size()); |
| while (!tx_pending_reqs_.empty()) { |
| CompleteTxn(tx_pending_reqs_.front().completer, ZX_ERR_CANCELED); |
| tx_pending_reqs_.pop(); |
| } |
| |
| // Disconnect USB. |
| // TODO(b/417808660): Replace logs with Inspect once the bug is fixed. |
| zxlogf(INFO, "Disconnecting from USB by deconfiguring"); |
| fidl::Result deconfigure = function_->Deconfigure(); |
| if (deconfigure.is_error()) { |
| zxlogf(ERROR, "Failed to deconfigure: %s", |
| deconfigure.error_value().FormatDescription().c_str()); |
| } |
| if (usb_function_binding_.has_value()) { |
| usb_function_binding_->Unbind(); |
| usb_function_binding_.reset(); |
| } |
| |
| zxlogf(INFO, "state_ = State::kStoppingUsb"); |
| state_ = State::kStoppingUsb; |
| |
| CheckUsbStopComplete(); |
| } |
| |
| void UsbAdbDevice::SendQueued() { |
| if (state_ != State::kOnline) { |
| ZX_PANIC("Unexpected state: %d", state_); |
| } |
| while (SendQueuedOnce()) { |
| } |
| } |
| |
| // Returns true if any progress was made. Returns false if we didn't send |
| // anything, and therefore calling this again won't be useful until something |
| // changes. |
| bool UsbAdbDevice::SendQueuedOnce() { |
| if (tx_pending_reqs_.empty()) { |
| return false; |
| } |
| |
| auto& current = tx_pending_reqs_.front(); |
| std::vector<fuchsia_hardware_usb_request::Request> requests; |
| while (current.start < current.request.data().size()) { |
| auto req = bulk_in_ep_.GetRequest(); |
| if (!req) { |
| break; |
| } |
| req->clear_buffers(); |
| |
| size_t to_copy = std::min(current.request.data().size() - current.start, kVmoDataSize); |
| auto actual = req->CopyTo(0, current.request.data().data() + current.start, to_copy, |
| bulk_in_ep_.GetMapped()); |
| size_t actual_total = 0; |
| for (size_t i = 0; i < actual.size(); i++) { |
| // Fill in size of data. |
| (*req)->data()->at(i).size(actual[i]); |
| actual_total += actual[i]; |
| } |
| auto status = req->CacheFlush(bulk_in_ep_.GetMapped()); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Cache flush failed %s", zx_status_get_string(status)); |
| } |
| |
| requests.emplace_back(req->take_request()); |
| current.start += actual_total; |
| } |
| |
| if (requests.empty()) { |
| return false; |
| } |
| auto result = bulk_in_ep_->QueueRequests(std::move(requests)); |
| if (result.is_error()) { |
| zxlogf(ERROR, "Failed to QueueRequests %s", result.error_value().FormatDescription().c_str()); |
| } |
| |
| if (current.start == current.request.data().size()) { |
| CompleteTxn(current.completer, ZX_OK); |
| tx_pending_reqs_.pop(); |
| } |
| |
| return true; |
| } |
| |
| void UsbAdbDevice::ReceiveQueued() { |
| if (state_ != State::kOnline) { |
| ZX_PANIC("Unexpected state: %d", state_); |
| } |
| while (ReceiveQueuedOnce()) { |
| } |
| } |
| |
| bool UsbAdbDevice::ReceiveQueuedOnce() { |
| if (pending_replies_.empty() || rx_requests_.empty()) { |
| return false; |
| } |
| |
| auto completion = std::move(pending_replies_.front()); |
| pending_replies_.pop(); |
| |
| auto req = usb::FidlRequest(std::move(completion.request().value())); |
| |
| if (*completion.status() != ZX_OK) { |
| zxlogf(ERROR, "RxComplete called with error %s.", zx_status_get_string(*completion.status())); |
| rx_requests_.front().Reply(fit::error(ZX_ERR_INTERNAL)); |
| } else { |
| // This should always be true because when we registered VMOs, we only registered one per |
| // request. |
| ZX_ASSERT(req->data()->size() == 1); |
| auto addr = bulk_out_ep_.GetMappedAddr(req.request(), 0); |
| if (!addr.has_value()) { |
| zxlogf(ERROR, "Failed to get mapped"); |
| rx_requests_.front().Reply(fit::error(ZX_ERR_INTERNAL)); |
| } else { |
| auto status = req.CacheFlushInvalidate(bulk_out_ep_.GetMapped()); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Cache flush and invalidate failed %s", zx_status_get_string(status)); |
| } |
| rx_requests_.front().Reply(fit::ok( |
| std::vector<uint8_t>(reinterpret_cast<uint8_t*>(*addr), |
| reinterpret_cast<uint8_t*>(*addr) + *completion.transfer_size()))); |
| } |
| } |
| rx_requests_.pop(); |
| |
| req.reset_buffers(bulk_out_ep_.GetMapped()); |
| |
| std::vector<fuchsia_hardware_usb_request::Request> requests; |
| requests.emplace_back(req.take_request()); |
| auto result = bulk_out_ep_->QueueRequests(std::move(requests)); |
| if (result.is_error()) { |
| zxlogf(ERROR, "Failed to QueueRequests %s", result.error_value().FormatDescription().c_str()); |
| } |
| |
| return true; |
| } |
| |
| void UsbAdbDevice::QueueTx(QueueTxRequest& request, QueueTxCompleter::Sync& completer) { |
| size_t length = request.data().size(); |
| if (length == 0) { |
| zxlogf(INFO, "Invalid argument - Length = 0"); |
| completer.Reply(fit::error(ZX_ERR_INVALID_ARGS)); |
| return; |
| } |
| |
| switch (state_) { |
| case State::kStoppingUsb: |
| // Return early during shutdown. |
| completer.Reply(fit::error(ZX_ERR_BAD_STATE)); |
| return; |
| case State::kOnline: |
| case State::kAwaitingUsbConnection: |
| tx_pending_reqs_.emplace( |
| txn_req_t{.request = std::move(request), .start = 0, .completer = completer.ToAsync()}); |
| SendQueued(); |
| } |
| } |
| |
| void UsbAdbDevice::Receive(ReceiveCompleter::Sync& completer) { |
| switch (state_) { |
| case State::kStoppingUsb: |
| // Return early during shutdown. |
| completer.Reply(fit::error(ZX_ERR_BAD_STATE)); |
| return; |
| case State::kAwaitingUsbConnection: |
| rx_requests_.emplace(completer.ToAsync()); |
| break; |
| case State::kOnline: |
| rx_requests_.emplace(completer.ToAsync()); |
| ReceiveQueued(); |
| break; |
| } |
| } |
| |
| void UsbAdbDevice::RxComplete(std::vector<fendpoint::Completion> completions) { |
| for (auto& completion : completions) { |
| // This should always be true because when we registered VMOs, we only registered one per |
| // request. |
| ZX_ASSERT(completion.request()->data()->size() == 1); |
| |
| switch (state_) { |
| case State::kAwaitingUsbConnection: |
| ZX_PANIC("Completion arrived before we sent any requests?"); |
| case State::kStoppingUsb: |
| bulk_out_ep_.PutRequest(usb::FidlRequest(std::move(completion.request().value()))); |
| CheckUsbStopComplete(); |
| break; |
| case State::kOnline: |
| pending_replies_.push(std::move(completion)); |
| ReceiveQueued(); |
| break; |
| } |
| } |
| } |
| |
| void UsbAdbDevice::TxComplete(std::vector<fendpoint::Completion> completions) { |
| for (auto& completion : completions) { |
| switch (state_) { |
| case State::kAwaitingUsbConnection: |
| ZX_PANIC("Completion arrived before we sent any requests?"); |
| case State::kStoppingUsb: |
| bulk_in_ep_.PutRequest(usb::FidlRequest(std::move(completion.request().value()))); |
| CheckUsbStopComplete(); |
| break; |
| case State::kOnline: |
| bulk_in_ep_.PutRequest(usb::FidlRequest(std::move(completion.request().value()))); |
| |
| // Do not queue requests if status is ZX_ERR_IO_NOT_PRESENT, as the underlying connection |
| // could be disconnected or USB_RESET is being processed. Calling adb_send_locked in such |
| // scenario will deadlock and crash the driver (see https://fxbug.dev/42174506). |
| if (*completion.status() != ZX_ERR_IO_NOT_PRESENT) { |
| SendQueued(); |
| } |
| break; |
| } |
| } |
| } |
| |
| void UsbAdbDevice::Control(ControlRequest& request, ControlCompleter::Sync& completer) { |
| completer.Reply(zx::ok(std::vector<uint8_t>{})); |
| } |
| |
| void UsbAdbDevice::EnableEndpoints() { |
| switch (state_) { |
| case State::kOnline: |
| zxlogf(INFO, "USB endpoints already enabled"); |
| return; |
| case State::kStoppingUsb: |
| zxlogf(ERROR, "This is unexpected: UsbFunctionInterface is disconnected while stopping"); |
| return; |
| case State::kAwaitingUsbConnection: |
| zxlogf(INFO, "Enabling USB endpoints"); |
| break; |
| } |
| |
| fuchsia_hardware_usb_function::EndpointConfiguration ep_config_out; |
| { |
| fuchsia_hardware_usb_function::EndpointDescriptor desc; |
| desc.bm_attributes(descriptors_.bulk_out_ep.bm_attributes); |
| desc.w_max_packet_size(le16toh(descriptors_.bulk_out_ep.w_max_packet_size)); |
| desc.b_interval(descriptors_.bulk_out_ep.b_interval); |
| ep_config_out.descriptor(std::move(desc)); |
| } |
| fidl::Result result_out = function_->ConfigureEndpoint( |
| {descriptors_.bulk_out_ep.b_endpoint_address, std::move(ep_config_out)}); |
| if (result_out.is_error()) { |
| ZX_PANIC("Failed to Config BULK OUT ep: %s", |
| result_out.error_value().FormatDescription().c_str()); |
| } |
| |
| fuchsia_hardware_usb_function::EndpointConfiguration ep_config_in; |
| { |
| fuchsia_hardware_usb_function::EndpointDescriptor desc; |
| desc.bm_attributes(descriptors_.bulk_in_ep.bm_attributes); |
| desc.w_max_packet_size(le16toh(descriptors_.bulk_in_ep.w_max_packet_size)); |
| desc.b_interval(descriptors_.bulk_in_ep.b_interval); |
| ep_config_in.descriptor(std::move(desc)); |
| } |
| fidl::Result result_in = function_->ConfigureEndpoint( |
| {descriptors_.bulk_in_ep.b_endpoint_address, std::move(ep_config_in)}); |
| if (result_in.is_error()) { |
| ZX_PANIC("Failed to Config BULK IN ep: %s", |
| result_in.error_value().FormatDescription().c_str()); |
| } |
| |
| // queue RX requests |
| std::vector<fuchsia_hardware_usb_request::Request> requests; |
| while (auto req = bulk_out_ep_.GetRequest()) { |
| req->reset_buffers(bulk_out_ep_.GetMapped()); |
| auto status = req->CacheFlushInvalidate(bulk_out_ep_.GetMapped()); |
| if (status != ZX_OK) { |
| ZX_PANIC("Cache flush and invalidate failed %d", status); |
| } |
| |
| requests.emplace_back(req->take_request()); |
| } |
| auto result = bulk_out_ep_->QueueRequests(std::move(requests)); |
| if (result.is_error()) { |
| ZX_PANIC("Failed to QueueRequests %s", result.error_value().FormatDescription().c_str()); |
| } |
| |
| if (adb_binding_.has_value()) { |
| auto result = fidl::WireSendEvent(*adb_binding_)->OnStatusChanged(fadb::StatusFlags::kOnline); |
| if (!result.ok()) { |
| zxlogf(ERROR, "Could not call UsbAdbImpl.OnStatusChanged."); |
| } |
| } |
| |
| zxlogf(INFO, "state_ = State::kOnline"); |
| state_ = State::kOnline; |
| } |
| |
| void UsbAdbDevice::SetConfigured(SetConfiguredRequest& request, |
| SetConfiguredCompleter::Sync& completer) { |
| zxlogf(INFO, "configured? - %d", request.configured()); |
| if (request.configured()) { |
| EnableEndpoints(); |
| } else { |
| switch (state_) { |
| case State::kAwaitingUsbConnection: |
| // It's normal to receive SetConfigured(false) while the connection is |
| // starting up - ignore it. |
| break; |
| case State::kOnline: |
| ResetOrStopUsb(); |
| break; |
| case State::kStoppingUsb: |
| zxlogf( |
| WARNING, |
| "Received SetConfigured(false) while stopping. This is unexpected, but probably fine."); |
| break; |
| } |
| } |
| completer.Reply(zx::ok()); |
| } |
| |
| void UsbAdbDevice::SetInterface(SetInterfaceRequest& request, |
| SetInterfaceCompleter::Sync& completer) { |
| zxlogf(INFO, "SetInterface called"); |
| completer.Reply(zx::ok()); |
| } |
| |
| void UsbAdbDevice::handle_unknown_method( |
| fidl::UnknownMethodMetadata<fuchsia_hardware_usb_function::UsbFunctionInterface> metadata, |
| fidl::UnknownMethodCompleter::Sync& completer) { |
| zxlogf(WARNING, "Unknown method %ld", metadata.method_ordinal); |
| } |
| |
| void UsbAdbDevice::CheckUsbStopComplete() { |
| if (state_ != State::kStoppingUsb) { |
| ZX_PANIC("Unexpected state: %d", state_); |
| } |
| |
| if (!bulk_in_ep_.RequestsFull() || !bulk_out_ep_.RequestsFull()) { |
| // Still waiting for outstanding USB requests to return. |
| // TODO(b/417808660): Replace logs with Inspect once the bug is fixed. |
| zxlogf(INFO, "Not all USB requests complete (in:%d out:%d)", bulk_in_ep_.RequestsFull(), |
| bulk_out_ep_.RequestsFull()); |
| return; |
| } |
| |
| zxlogf(INFO, "All USB requests complete. Completing USB stop."); |
| |
| if (adb_binding_.has_value()) { |
| auto result = fidl::WireSendEvent(*adb_binding_)->OnStatusChanged(fadb::StatusFlags(0)); |
| if (!result.ok()) { |
| zxlogf(ERROR, "Could not call UsbAdbImpl.OnStatusChanged."); |
| } |
| } |
| |
| adb_binding_.reset(); |
| |
| // TODO(b/417808660): Replace logs with Inspect once the bug is fixed. |
| zxlogf(INFO, "Calling stop_completers_"); |
| while (!stop_completers_.empty()) { |
| stop_completers_.back().Reply(zx::ok()); |
| stop_completers_.pop_back(); |
| } |
| |
| // Is this a proper shutdown, or a restart of USB? |
| if (shutdown_callback_.has_value()) { |
| bulk_out_ep_.Close(); |
| bulk_in_ep_.Close(); |
| zxlogf(INFO, "Shutting down driver."); |
| shutdown_callback_.value()(zx::ok()); |
| shutdown_callback_.reset(); |
| } else { |
| zxlogf(INFO, "Restarting USB connection."); |
| StartUsb(); |
| } |
| } |
| |
| void UsbAdbDevice::PrepareStop(fdf::PrepareStopCompleter completer) { |
| shutdown_callback_.emplace(std::move(completer)); |
| ResetOrStopUsb(); |
| } |
| |
| zx_status_t UsbAdbDevice::InitEndpoint( |
| fidl::ClientEnd<fuchsia_hardware_usb_endpoint::Endpoint> endpoint_client, |
| usb::EndpointClient<UsbAdbDevice>& ep, uint32_t req_count) { |
| zx_status_t status = ep.Init(std::move(endpoint_client), dispatcher()); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Failed to init UsbEndpoint %s", zx_status_get_string(status)); |
| return status; |
| } |
| |
| // TODO(127854): When we support active pinning of VMOs, adb may want to use VMOs that are not |
| // perpetually pinned. |
| auto actual = |
| ep.AddRequests(req_count, kVmoDataSize, fuchsia_hardware_usb_request::Buffer::Tag::kVmoId); |
| if (actual != req_count) { |
| zxlogf(ERROR, "Wanted %u requests, only got %zu requests", req_count, actual); |
| } |
| return actual == 0 ? ZX_ERR_INTERNAL : ZX_OK; |
| } |
| |
| zx::result<> UsbAdbDevice::Start() { |
| auto client = incoming()->Connect<fuchsia_hardware_usb_function::UsbFunctionService::Device>(); |
| |
| if (client.is_error()) { |
| zxlogf(ERROR, "Failed to connect fidl protocol"); |
| return client.take_error(); |
| } |
| function_.Bind(std::move(*client)); |
| |
| zx::result bulk_out_endpoints = fidl::CreateEndpoints<fuchsia_hardware_usb_endpoint::Endpoint>(); |
| if (bulk_out_endpoints.is_error()) { |
| return bulk_out_endpoints.take_error(); |
| } |
| zx::result bulk_in_endpoints = fidl::CreateEndpoints<fuchsia_hardware_usb_endpoint::Endpoint>(); |
| if (bulk_in_endpoints.is_error()) { |
| return bulk_in_endpoints.take_error(); |
| } |
| |
| std::vector<fuchsia_hardware_usb_function::EndpointResource> resources; |
| { |
| fuchsia_hardware_usb_function::EndpointResource res; |
| res.direction(fuchsia_hardware_usb_function::EndpointDirection::kOut); |
| res.endpoint(std::move(bulk_out_endpoints->server)); |
| resources.emplace_back(std::move(res)); |
| } |
| { |
| fuchsia_hardware_usb_function::EndpointResource res; |
| res.direction(fuchsia_hardware_usb_function::EndpointDirection::kIn); |
| res.endpoint(std::move(bulk_in_endpoints->server)); |
| resources.emplace_back(std::move(res)); |
| } |
| |
| fidl::Request<fuchsia_hardware_usb_function::UsbFunction::AllocResources> alloc_req; |
| alloc_req.interface_count(1); |
| alloc_req.endpoints(std::move(resources)); |
| |
| fidl::Result alloc_result = function_->AllocResources(std::move(alloc_req)); |
| if (alloc_result.is_error()) { |
| zxlogf(ERROR, "AllocResources failed: %s", |
| alloc_result.error_value().FormatDescription().c_str()); |
| return zx::error(alloc_result.error_value().is_framework_error() |
| ? alloc_result.error_value().framework_error().status() |
| : alloc_result.error_value().domain_error()); |
| } |
| |
| auto& response = alloc_result.value(); |
| descriptors_.adb_intf.b_interface_number = response.interface_nums()[0]; |
| descriptors_.bulk_out_ep.b_endpoint_address = response.endpoint_addrs()[0]; |
| descriptors_.bulk_in_ep.b_endpoint_address = response.endpoint_addrs()[1]; |
| |
| zx_status_t status = |
| InitEndpoint(std::move(bulk_out_endpoints->client), bulk_out_ep_, kBulkRxCount); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "InitEndpoint failed - %s.", zx_status_get_string(status)); |
| return zx::error(status); |
| } |
| status = InitEndpoint(std::move(bulk_in_endpoints->client), bulk_in_ep_, kBulkTxCount); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "InitEndpoint failed - %s.", zx_status_get_string(status)); |
| return zx::error(status); |
| } |
| auto serve_result = outgoing()->AddService<fadb::Service>(fadb::Service::InstanceHandler({ |
| .adb = device_bindings_.CreateHandler(this, fdf::Dispatcher::GetCurrent()->async_dispatcher(), |
| fidl::kIgnoreBindingClosure), |
| })); |
| if (serve_result.is_error()) { |
| zxlogf(ERROR, "Failed to add Device service %s", serve_result.status_string()); |
| return serve_result.take_error(); |
| } |
| |
| StartUsb(); |
| return zx::ok(); |
| } |
| |
| void UsbAdbDevice::StartUsb() { |
| zx::result iface_endpoints = |
| fidl::CreateEndpoints<fuchsia_hardware_usb_function::UsbFunctionInterface>(); |
| if (iface_endpoints.is_error()) { |
| ZX_PANIC("CreateEndpoints failed %s", zx_status_get_string(iface_endpoints.error_value())); |
| } |
| usb_function_binding_ = fidl::BindServer(dispatcher(), std::move(iface_endpoints->server), this); |
| |
| std::vector<uint8_t> descriptors_buffer(sizeof(descriptors_)); |
| memcpy(descriptors_buffer.data(), &descriptors_, sizeof(descriptors_)); |
| |
| fidl::Request<fuchsia_hardware_usb_function::UsbFunction::Configure> config_req; |
| config_req.configuration(std::move(descriptors_buffer)); |
| config_req.iface(std::move(iface_endpoints->client)); |
| |
| fidl::Result config_res = function_->Configure(std::move(config_req)); |
| if (config_res.is_error()) { |
| ZX_PANIC("Configure failed: %s", config_res.error_value().FormatDescription().c_str()); |
| } |
| |
| zxlogf(INFO, "state_ = State::kAwaitingUsbConnection"); |
| state_ = State::kAwaitingUsbConnection; |
| } |
| |
| } // namespace usb_adb_function |
| |
| FUCHSIA_DRIVER_EXPORT(usb_adb_function::UsbAdbDevice); |