blob: b4cb1aaa3a08d1e0bd4846bdeb323ce2a82c85a7 [file] [log] [blame]
// Copyright 2016 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 "device.h"
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <ddk/debug.h>
#include <hw/inout.h>
#include <pretty/hexdump.h>
#include <zircon/status.h>
#include <utility>
#include "trace.h"
#define LOCAL_TRACE 0
namespace virtio {
Device::Device(zx_device_t* bus_device, zx::bti bti, fbl::unique_ptr<Backend> backend)
: bti_(std::move(bti)), backend_(std::move(backend)), bus_device_(bus_device) {
LTRACE_ENTRY;
device_ops_.version = DEVICE_OPS_VERSION;
}
Device::~Device() {
LTRACE_ENTRY;
}
void Device::Unbind() {
device_remove(device_);
}
void Device::Release() {
backend_.reset();
}
void Device::IrqWorker() {
zx_status_t rc;
zxlogf(TRACE, "%s: starting irq worker\n", tag());
while (backend_->InterruptValid() == ZX_OK) {
if ((rc = backend_->WaitForInterrupt()) != ZX_OK) {
zxlogf(SPEW, "%s: error while waiting for interrupt: %s\n",
tag(), zx_status_get_string(rc));
continue;
}
// Read the status before completing the interrupt in case
// another interrupt fires and changes the status.
uint32_t irq_status = IsrStatus();
LTRACEF_LEVEL(2, "irq_status %#x\n", irq_status);
// Since we handle both interrupt types here it's possible for a
// spurious interrupt if they come in sequence and we check IsrStatus
// after both have been triggered.
if (irq_status == 0)
continue;
if (irq_status & VIRTIO_ISR_QUEUE_INT) { /* used ring update */
IrqRingUpdate();
}
if (irq_status & VIRTIO_ISR_DEV_CFG_INT) { /* config change */
IrqConfigChange();
}
}
}
int Device::IrqThreadEntry(void* arg) {
Device* d = static_cast<Device*>(arg);
d->IrqWorker();
return 0;
}
void Device::StartIrqThread() {
thrd_create_with_name(&irq_thread_, IrqThreadEntry, this, "virtio-irq-thread");
thrd_detach(irq_thread_);
}
zx_status_t Device::CopyDeviceConfig(void* _buf, size_t len) const {
assert(_buf);
for (uint16_t i = 0; i < len; i++) {
backend_->ReadDeviceConfig(i, static_cast<uint8_t*>(_buf) + i);
}
return ZX_OK;
}
} // namespace virtio