text: // 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.
  
  #ifndef SRC_DEVICES_BLOCK_DRIVERS_VIRTIO_SCSI_H_
  #define SRC_DEVICES_BLOCK_DRIVERS_VIRTIO_SCSI_H_
  
  #include <lib/scsi/scsilib_controller.h>
  #include <lib/sync/completion.h>
  #include <lib/virtio/backends/backend.h>
  #include <lib/virtio/device.h>
  #include <lib/virtio/ring.h>
  #include <lib/zircon-internal/thread_annotations.h>
  #include <stdlib.h>
  #include <sys/uio.h>
  #include <zircon/compiler.h>
  
  #include <atomic>
  #include <memory>
  
  #include <ddktl/device.h>
  #include <fbl/auto_lock.h>
  #include <fbl/condition_variable.h>
  #include <virtio/scsi.h>
  
  namespace virtio {
  
  constexpr int MAX_IOS = 16;
  
  class ScsiDevice : public virtio::Device,
                     public scsi::Controller,
                     public ddk::Device<ScsiDevice, ddk::Unbindable> {
   public:
    enum Queue {
      CONTROL = 0,
      EVENT = 1,
      REQUEST = 2,
    };
  
    ScsiDevice(zx_device_t* device, zx::bti bti, std::unique_ptr<Backend> backend)
        : virtio::Device(device, std::move(bti), std::move(backend)),
          ddk::Device<ScsiDevice, ddk::Unbindable>(device) {}
  
    // virtio::Device overrides
    zx_status_t Init() override;
    void DdkUnbind(ddk::UnbindTxn txn);
    void DdkRelease();
    // Invoked for most device interrupts.
    virtual void IrqRingUpdate() override;
    // Invoked on config change interrupts.
    void IrqConfigChange() override {}
  
    const char* tag() const override { return "virtio-scsi"; }
  
    static void FillLUNStructure(struct virtio_scsi_req_cmd* req, uint8_t target, uint16_t lun);
  
   private:
    zx_status_t TargetMaxXferSize(uint8_t target, uint16_t lun, uint32_t& xfer_size_sectors);
  
    zx_status_t ExecuteCommandSync(uint8_t target, uint16_t lun, struct iovec cdb,
                                   struct iovec data_out, struct iovec data_in) override;
  
    zx_status_t ExecuteCommandAsync(uint8_t target, uint16_t lun, struct iovec cdb,
                                    struct iovec data_out, struct iovec data_in,
                                    void (*cb)(void*, zx_status_t), void* cookie) override;
    zx_status_t WorkerThread();
  
    // Latched copy of virtio-scsi device configuration.
    struct virtio_scsi_config config_ TA_GUARDED(lock_) = {};
  
    struct scsi_io_slot {
      io_buffer_t request_buffer;
      bool avail;
      vring_desc* tail_desc;
      void* cookie;
      void (*callback)(void* cookie, zx_status_t status);
      struct iovec data_in;
      void* data_in_region;
      io_buffer_t* request_buffers;
      struct virtio_scsi_resp_cmd* response;
    };
    scsi_io_slot* GetIO() TA_REQ(lock_);
    void FreeIO(scsi_io_slot* io_slot) TA_REQ(lock_);
    size_t request_buffers_size_;
    scsi_io_slot scsi_io_slot_table_[MAX_IOS] TA_GUARDED(lock_) = {};
  
    Ring control_ring_ TA_GUARDED(lock_) = {this};
    Ring request_queue_ = {this};
  
    thrd_t worker_thread_;
    bool worker_thread_should_exit_ TA_GUARDED(lock_) = {};
  
    // Synchronizes virtio rings and worker thread control.
    fbl::Mutex lock_;
  
    // We use the condvar to control the number of IO's in flight
    // as well as to wait for descs to become available.
    fbl::ConditionVariable ioslot_cv_ __TA_GUARDED(lock_);
    fbl::ConditionVariable desc_cv_ __TA_GUARDED(lock_);
    uint32_t active_ios_ __TA_GUARDED(lock_);
    uint64_t scsi_transport_tag_ __TA_GUARDED(lock_);
  };
  
  }  // namespace virtio
  
  #endif  // SRC_DEVICES_BLOCK_DRIVERS_VIRTIO_SCSI_H_
  