blob: d8e397f0910906a1cff410c9ff5f0ee2c8193f81 [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.
#ifndef SRC_DEVICES_BUS_LIB_VIRTIO_INCLUDE_LIB_VIRTIO_DEVICE_H_
#define SRC_DEVICES_BUS_LIB_VIRTIO_INCLUDE_LIB_VIRTIO_DEVICE_H_
#include <lib/virtio/backends/backend.h>
#include <lib/zx/bti.h>
#include <lib/zx/handle.h>
#include <threads.h>
#include <zircon/types.h>
#include <atomic>
#include <memory>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/protocol/pci.h>
#include <fbl/mutex.h>
#include <virtio/virtio.h>
// Virtio devices are represented by a derived class specific to their type (eg
// gpu) with a virtio::Device base. The device class handles general work around
// IRQ handling and contains a backend that is instantiated at creation time
// that implements a virtio backend. This allows a single device driver to work
// on both Virtio legacy or transistional without needing to special case the
// device interaction.
namespace virtio {
class Device {
public:
Device(zx_device_t* bus_device, zx::bti bti, std::unique_ptr<Backend> backend);
virtual ~Device();
virtual zx_status_t Init() = 0;
virtual void Release();
virtual void Unbind(ddk::UnbindTxn txn);
void StartIrqThread();
// interrupt cases that devices may override
pci_irq_mode_t InterruptMode() { return backend_->InterruptMode(); }
virtual void IrqRingUpdate() = 0;
virtual void IrqConfigChange() = 0;
// Get the Ring size for the particular device / backend.
// This has to be proxied to a backend method because we can't
// simply do config reads to determine the information.
uint16_t GetRingSize(uint16_t index) { return backend_->GetRingSize(index); }
// Set up ring descriptors with the backend.
void SetRing(uint16_t index, uint16_t count, zx_paddr_t pa_desc, zx_paddr_t pa_avail,
zx_paddr_t pa_used) {
backend_->SetRing(index, count, pa_desc, pa_avail, pa_used);
}
// Another method that has to be proxied to the backend due to differences
// in how Legacy vs Modern systems are laid out.
void RingKick(uint16_t ring_index) { backend_->RingKick(ring_index); }
// It is expected that each derived device will implement tag().
zx_device_t* device() { return device_; }
virtual const char* tag() const = 0; // Implemented by derived devices
// Accessor for bti so that Rings can map IO buffers
const zx::bti& bti() { return bti_; }
protected:
// Methods for checking / acknowledging features
bool DeviceFeatureSupported(uint32_t feature) { return backend_->ReadFeature(feature); }
void DriverFeatureAck(uint32_t feature) { backend_->SetFeature(feature); }
bool DeviceStatusFeaturesOk() { return backend_->ConfirmFeatures(); }
// Devie lifecycle methods
void DeviceReset() { backend_->DeviceReset(); }
void DriverStatusAck() { backend_->DriverStatusAck(); }
void DriverStatusOk() { backend_->DriverStatusOk(); }
uint32_t IsrStatus() const { return backend_->IsrStatus(); }
// Device config management
zx_status_t CopyDeviceConfig(void* _buf, size_t len) const;
template <typename T>
void ReadDeviceConfig(uint16_t offset, T* val) {
backend_->ReadDeviceConfig(offset, val);
}
template <typename T>
void WriteDeviceConfig(uint16_t offset, T val) {
backend_->WriteDeviceConfig(offset, val);
}
zx_device_t* bus_device() const { return bus_device_; }
static int IrqThreadEntry(void* arg);
void IrqWorker();
// BTI for managing DMA
zx::bti bti_;
// backend responsible for hardware io. Will be released when device goes out of scope
std::unique_ptr<Backend> backend_;
// irq thread object
thrd_t irq_thread_ = {};
// Bus device is the parent device on the bus, device is this driver's device node.
zx_device_t* bus_device_ = nullptr;
zx_device_t* device_ = nullptr;
// DDK device
// TODO: It might make sense for the base device class to be the one
// to handle device_add() calls rather than delegating it to the derived
// instances of devices.
zx_protocol_device_t device_ops_ = {};
// This lock exists for devices to synchronize themselves, it should not be used by the base
// device class.
fbl::Mutex lock_;
std::atomic<bool> irq_thread_should_exit_ = false;
};
} // namespace virtio
#endif // SRC_DEVICES_BUS_LIB_VIRTIO_INCLUDE_LIB_VIRTIO_DEVICE_H_