blob: 01553313ea33e39c7c4de5749f652c4dae277c37 [file] [log] [blame]
// Copyright 2021 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.
// TODO(rsakthi) - how to get this from bazel?
#define CPTCFG_IWLMVM 1
#include "src/iwlwifi/platform/pcie-device.h"
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <memory>
extern "C" {
#include "src/iwlwifi/iwl-debug.h"
#include "src/iwlwifi/iwl-drv.h"
#include "src/iwlwifi/pcie/entry.h"
}
#include "src/iwlwifi/platform/driver-inspector.h"
#if !CPTCFG_IWLMVM
#error "PcieDevice requires support for MVM firmwares."
#endif // CPTCFG_IWLMVM
namespace wlan {
namespace iwlwifi {
PcieDevice::PcieDevice(zx_device_t* parent) : WlanphyImplDevice(parent) { pci_dev_ = {}; }
PcieDevice::~PcieDevice() { ZX_DEBUG_ASSERT(pci_dev_.drvdata == nullptr); }
zx_status_t PcieDevice::Create(zx_device_t* parent_device) {
zx_status_t status = ZX_OK;
std::unique_ptr<PcieDevice> device(new PcieDevice(parent_device));
device->driver_inspector_ =
std::make_unique<DriverInspector>(DriverInspectorOptions{.root_name = "iwlwifi"});
if ((status = device->DdkAdd(::ddk::DeviceAddArgs("iwlwifi-wlanphyimpl")
.set_inspect_vmo(device->driver_inspector_->DuplicateVmo()))) !=
ZX_OK) {
IWL_ERR(device->drvdata(), "%s() failed device add: %s", __func__,
zx_status_get_string(status));
return status;
}
// The lifecycle of this object is now managed by the devhost.
device.release();
return ZX_OK;
}
iwl_trans* PcieDevice::drvdata() { return pci_dev_.drvdata; }
const iwl_trans* PcieDevice::drvdata() const { return pci_dev_.drvdata; }
void PcieDevice::DdkInit(::ddk::InitTxn txn) {
const zx_status_t status = [&]() {
zx_status_t status = ZX_OK;
task_loop_ = std::make_unique<::async::Loop>(&kAsyncLoopConfigNoAttachToCurrentThread);
if ((status = task_loop_->StartThread("iwlwifi-task-worker", nullptr)) != ZX_OK) {
IWL_ERR(iwl_trans, "Failed to create task async loop thread: %s\n",
zx_status_get_string(status));
return status;
}
irq_loop_ = std::make_unique<::async::Loop>(&kAsyncLoopConfigNoAttachToCurrentThread);
if ((status = irq_loop_->StartThread("iwlwifi-irq-worker", nullptr)) != ZX_OK) {
IWL_ERR(iwl_trans, "Failed to create irq async loop thread: %s\n",
zx_status_get_string(status));
return status;
}
// Fill in the relevant Fuchsia-specific fields in our driver interface struct.
pci_dev_.dev.zxdev = zxdev();
pci_dev_.dev.task_dispatcher = task_loop_->dispatcher();
pci_dev_.dev.irq_dispatcher = irq_loop_->dispatcher();
pci_dev_.dev.inspector = static_cast<struct driver_inspector*>(driver_inspector_.get());
if ((status = device_get_fragment_protocol(parent(), "pci", ZX_PROTOCOL_PCI,
&pci_dev_.proto)) != ZX_OK) {
return status;
}
// Perform Fuchsia-specific PCI initialization.
if ((status = pci_get_bti(&pci_dev_.proto, /*index*/ 0, &pci_dev_.dev.bti)) != ZX_OK) {
IWL_ERR(nullptr, "Failed to get PCI BTI: %s\n", zx_status_get_string(status));
return status;
}
pcie_device_info_t pci_info = {};
if ((status = pci_get_device_info(&pci_dev_.proto, &pci_info)) != ZX_OK) {
return status;
}
uint16_t subsystem_device_id = 0;
if ((status = pci_config_read16(&pci_dev_.proto, PCI_CFG_SUBSYSTEM_ID, &subsystem_device_id)) !=
ZX_OK) {
IWL_ERR(nullptr, "Failed to read PCI subsystem device ID: %s\n",
zx_status_get_string(status));
return status;
}
IWL_INFO(nullptr, "Device ID: %04x Subsystem Device ID: %04x\n", pci_info.device_id,
subsystem_device_id);
if ((status = iwl_drv_init()) != ZX_OK) {
IWL_ERR(nullptr, "Failed to init driver: %s\n", zx_status_get_string(status));
return status;
}
const iwl_pci_device_id* id = nullptr;
if ((status = iwl_pci_find_device_id(pci_info.device_id, subsystem_device_id, &id)) != ZX_OK) {
IWL_ERR(nullptr, "Failed to find PCI config: %s\n", zx_status_get_string(status));
return status;
}
if ((status = iwl_pci_probe(&pci_dev_, id)) != ZX_OK) {
IWL_ERR(nullptr, "Failed to probe PCI device: %s\n", zx_status_get_string(status));
return status;
}
return ZX_OK;
}();
txn.Reply(status);
return;
}
void PcieDevice::DdkUnbind(::ddk::UnbindTxn txn) {
iwl_pci_remove(&pci_dev_);
zx_handle_close(pci_dev_.dev.bti);
pci_dev_.dev.bti = ZX_HANDLE_INVALID;
irq_loop_->Shutdown();
task_loop_->Shutdown();
txn.Reply();
}
} // namespace iwlwifi
} // namespace wlan