blob: d25c42f937b3f1dcef36856acc1603fa93247ec0 [file] [log] [blame]
// Copyright 2019 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 <fcntl.h>
#include <fuchsia/hardware/telephony/transport/llcpp/fidl.h>
#include <fuchsia/hardware/usb/peripheral/llcpp/fidl.h>
#include <fuchsia/hardware/usb/virtual/bus/llcpp/fidl.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/watcher.h>
#include <lib/gtest/real_loop_fixture.h>
#include <lib/zx/channel.h>
#include <lib/zx/port.h>
#include <unistd.h>
#include <zircon/hw/usb.h>
#include <fbl/string.h>
#include <gtest/gtest.h>
#include <src/lib/isolated_devmgr/usb-virtual-bus.h>
constexpr uint32_t kQmiBufSize = 2048LU;
namespace {
/// hardcoded QMI request/response
constexpr uint8_t kQmiImeiReq[]{1, 12, 0, 0, 2, 1, 0, 1, 0, 37, 0, 0, 0};
constexpr uint8_t kQmiImeiResp[]{1, 41, 0, 128, 2, 1, 2, 1, 0, 37, 0, 29, 0, 2,
4, 0, 0, 0, 0, 0, 16, 1, 0, 48, 17, 15, 0, 51,
53, 57, 50, 54, 48, 48, 56, 48, 49, 54, 56, 51, 53, 49};
const std::string isolated_devmgr_pkg_url =
"fuchsia-pkg://fuchsia.com/telephony-tests#meta/tel-devmgr.cmx";
const std::string isolated_devmgr_svc_name = "fuchsia.tel.devmgr.IsolatedDevmgr";
class USBVirtualBusQmi : public usb_virtual_bus::USBVirtualBusBase {
public:
USBVirtualBusQmi() : USBVirtualBusBase(isolated_devmgr_pkg_url, isolated_devmgr_svc_name) {}
// Initialize QMI. Asserts on failure.
void InitUsbQmi(fbl::String* devpath);
};
void USBVirtualBusQmi::InitUsbQmi(fbl::String* devpath) {
namespace usb_peripheral = ::llcpp::fuchsia::hardware::usb::peripheral;
usb_peripheral::DeviceDescriptor device_desc = {};
device_desc.bcd_usb = htole16(0x0200);
device_desc.b_device_class = 0;
device_desc.b_device_sub_class = 0;
device_desc.b_device_protocol = 0;
device_desc.b_max_packet_size0 = 64;
device_desc.bcd_device = htole16(0x0100);
device_desc.b_num_configurations = 1;
device_desc.id_vendor = htole16(0x1199);
device_desc.id_product = htole16(0x9091);
usb_peripheral::FunctionDescriptor qmi_function_desc = {
.interface_class = USB_CLASS_VENDOR,
.interface_subclass = USB_SUBCLASS_VENDOR,
.interface_protocol = 0xff,
};
std::vector<usb_peripheral::FunctionDescriptor> function_descs_vec;
function_descs_vec.push_back(qmi_function_desc);
SetupPeripheralDevice(std::move(device_desc), std::move(function_descs_vec));
fbl::unique_fd fd(openat(devfs_root().get(), "class/qmi-transport", O_RDONLY));
while (fdio_watch_directory(fd.get(), usb_virtual_bus_helper::WaitForAnyFile, ZX_TIME_INFINITE,
devpath) != ZX_ERR_STOP)
;
*devpath = fbl::String::Concat({fbl::String("class/qmi-transport/"), *devpath});
}
class UsbQmiTest : public ::gtest::RealLoopFixture {
public:
void SetUp() override {
bus_.InitPeripheral();
bus_.InitUsbQmi(&devpath_);
}
void TearDown() override {
ASSERT_NO_FATAL_FAILURE(bus_.ClearPeripheralDeviceFunctions());
auto result2 = bus_.virtual_bus().Disable();
ASSERT_EQ(result2.status(), ZX_OK);
ASSERT_EQ(result2.value().status, ZX_OK);
}
USBVirtualBusQmi& GetVirtBus() { return bus_; };
fbl::String& GetDevPath() { return devpath_; };
private:
USBVirtualBusQmi bus_;
fbl::String devpath_;
};
TEST_F(UsbQmiTest, RequestImei) {
fbl::unique_fd fd_qmi(openat(GetVirtBus().GetRootFd(), GetDevPath().c_str(), O_RDWR));
ASSERT_GT(fd_qmi.get(), 0);
fdio_cpp::FdioCaller qmi_fdio_caller_;
qmi_fdio_caller_.reset(std::move(fd_qmi));
zx::port channel_port;
// set QMI channel to driver
zx::channel channel_local, channel_remote;
ASSERT_EQ(zx::channel::create(0, &channel_local, &channel_remote), ZX_OK);
auto result = llcpp::fuchsia::hardware::telephony::transport::Qmi::Call::SetChannel(
qmi_fdio_caller_.channel(), std::move(channel_remote));
ASSERT_EQ(result.status(), ZX_OK);
ASSERT_EQ(result->result.is_err(), false);
ASSERT_EQ(zx::port::create(0, &channel_port), ZX_OK);
channel_local.wait_async(channel_port, 0, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, 0);
// send QMI message requesting IMEI
ASSERT_EQ(channel_local.write(0, kQmiImeiReq, sizeof(kQmiImeiReq), nullptr, 0), ZX_OK);
// validate QMI message for IMEI reply
zx_port_packet_t packet;
ASSERT_EQ(channel_port.wait(zx::time(ZX_TIME_INFINITE), &packet), ZX_OK);
ASSERT_EQ(packet.key, 0LU);
ASSERT_EQ(packet.signal.observed & ZX_CHANNEL_PEER_CLOSED, 0L);
std::array<uint8_t, kQmiBufSize> buffer;
uint32_t read_bytes;
uint32_t handle_num;
channel_local.read(0, buffer.data(), nullptr, kQmiBufSize, 0, &read_bytes, &handle_num);
ASSERT_EQ(handle_num, 0LU);
ASSERT_EQ(memcmp(buffer.data(), kQmiImeiResp, sizeof(kQmiImeiResp)), 0L);
}
} // namespace