Virtio: added a driver with basic support for SCSI devices.
* Here is the Qemu command line option for Virtio SCSI devices: -drive if=none,id=hd,file=haiku.image -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd * virtio_scsi.h is copied unchanged from FreeBSD, except for the _PACKED directive.
This commit is contained in:
@@ -4,3 +4,4 @@ SubInclude HAIKU_TOP src add-ons kernel busses scsi ahci ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses scsi 53c8xx ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses scsi buslogic ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses scsi usb ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel busses scsi virtio ;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel busses scsi virtio ;
|
||||
|
||||
UsePrivateHeaders drivers virtio ;
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
KernelAddon virtio_scsi :
|
||||
virtio_scsi.cpp
|
||||
VirtioSCSIController.cpp
|
||||
VirtioSCSIHelper.cpp
|
||||
VirtioSCSIRequest.cpp
|
||||
;
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright 2013, Jérôme Duval, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "VirtioSCSIPrivate.h"
|
||||
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
|
||||
const char *
|
||||
get_feature_name(uint32 feature)
|
||||
{
|
||||
switch (feature) {
|
||||
case VIRTIO_SCSI_F_INOUT:
|
||||
return "in out";
|
||||
case VIRTIO_SCSI_F_HOTPLUG:
|
||||
return "hotplug";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
VirtioSCSIController::VirtioSCSIController(device_node *node)
|
||||
:
|
||||
fNode(node),
|
||||
fVirtio(NULL),
|
||||
fVirtioDevice(NULL),
|
||||
fStatus(B_NO_INIT),
|
||||
fRequest(NULL)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
B_INITIALIZE_SPINLOCK(&fInterruptLock);
|
||||
fInterruptCondition.Init(this, "virtio scsi transfer");
|
||||
|
||||
// get the Virtio device from our parent's parent
|
||||
device_node *parent = gDeviceManager->get_parent_node(node);
|
||||
device_node *virtioParent = gDeviceManager->get_parent_node(parent);
|
||||
gDeviceManager->put_node(parent);
|
||||
|
||||
gDeviceManager->get_driver(virtioParent, (driver_module_info **)&fVirtio,
|
||||
(void **)&fVirtioDevice);
|
||||
gDeviceManager->put_node(virtioParent);
|
||||
|
||||
fVirtio->negociate_features(fVirtioDevice,
|
||||
0 /*VIRTIO_SCSI_F_HOTPLUG*/,
|
||||
&fFeatures, &get_feature_name);
|
||||
|
||||
fStatus = fVirtio->read_device_config(fVirtioDevice, 0, &fConfig,
|
||||
sizeof(struct virtio_scsi_config));
|
||||
if (fStatus != B_OK)
|
||||
return;
|
||||
|
||||
fConfig.sense_size = VIRTIO_SCSI_SENSE_SIZE;
|
||||
fConfig.cdb_size = VIRTIO_SCSI_CDB_SIZE;
|
||||
|
||||
fVirtio->write_device_config(fVirtioDevice,
|
||||
offsetof(struct virtio_scsi_config, sense_size), &fConfig.sense_size,
|
||||
sizeof(fConfig.sense_size));
|
||||
fVirtio->write_device_config(fVirtioDevice,
|
||||
offsetof(struct virtio_scsi_config, cdb_size), &fConfig.sense_size,
|
||||
sizeof(fConfig.cdb_size));
|
||||
|
||||
fRequest = new(std::nothrow) VirtioSCSIRequest(true);
|
||||
if (fRequest == NULL) {
|
||||
fStatus = B_NO_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
::virtio_queue virtioQueues[3];
|
||||
fStatus = fVirtio->alloc_queues(fVirtioDevice, 3, virtioQueues);
|
||||
if (fStatus != B_OK) {
|
||||
ERROR("queue allocation failed (%s)\n", strerror(fStatus));
|
||||
return;
|
||||
}
|
||||
|
||||
fControlVirtioQueue = virtioQueues[0];
|
||||
fEventVirtioQueue = virtioQueues[1];
|
||||
fRequestVirtioQueue = virtioQueues[2];
|
||||
|
||||
fStatus = fVirtio->setup_interrupt(fVirtioDevice, NULL, NULL);
|
||||
if (fStatus != B_OK) {
|
||||
ERROR("interrupt setup failed (%s)\n", strerror(fStatus));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
VirtioSCSIController::~VirtioSCSIController()
|
||||
{
|
||||
CALLED();
|
||||
delete fRequest;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtioSCSIController::InitCheck()
|
||||
{
|
||||
return fStatus;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtioSCSIController::SetBus(scsi_bus bus)
|
||||
{
|
||||
fBus = bus;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtioSCSIController::PathInquiry(scsi_path_inquiry *info)
|
||||
{
|
||||
info->hba_inquiry = SCSI_PI_TAG_ABLE;
|
||||
info->hba_misc = 0;
|
||||
info->sim_priv = 0;
|
||||
info->initiator_id = VIRTIO_SCSI_INITIATOR_ID;
|
||||
info->hba_queue_size = fConfig.cmd_per_lun != 0 ? fConfig.cmd_per_lun : 1;
|
||||
memset(info->vuhba_flags, 0, sizeof(info->vuhba_flags));
|
||||
|
||||
strlcpy(info->sim_vid, "Haiku", SCSI_SIM_ID);
|
||||
strlcpy(info->hba_vid, "VirtIO", SCSI_HBA_ID);
|
||||
|
||||
strlcpy(info->sim_version, "1.0", SCSI_VERS);
|
||||
strlcpy(info->hba_version, "1.0", SCSI_VERS);
|
||||
strlcpy(info->controller_family, "Virtio", SCSI_FAM_ID);
|
||||
strlcpy(info->controller_type, "Virtio", SCSI_TYPE_ID);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtioSCSIController::GetRestrictions(uint8 targetID, bool *isATAPI,
|
||||
bool *noAutoSense, uint32 *maxBlocks)
|
||||
{
|
||||
*isATAPI = false;
|
||||
*noAutoSense = true;
|
||||
*maxBlocks = fConfig.cmd_per_lun;
|
||||
}
|
||||
|
||||
|
||||
uchar
|
||||
VirtioSCSIController::ResetDevice(uchar targetID, uchar targetLUN)
|
||||
{
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtioSCSIController::ExecuteRequest(scsi_ccb *ccb)
|
||||
{
|
||||
status_t result = fRequest->Start(ccb);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
if (ccb->cdb[0] == SCSI_OP_REQUEST_SENSE && fRequest->HasSense()) {
|
||||
TRACE("request sense\n");
|
||||
fRequest->RequestSense();
|
||||
fRequest->Finish(false);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (ccb->target_id > fConfig.max_target) {
|
||||
ERROR("invalid target device\n");
|
||||
fRequest->SetStatus(SCSI_TID_INVALID);
|
||||
fRequest->Finish(false);
|
||||
return B_BAD_INDEX;
|
||||
}
|
||||
|
||||
if (ccb->target_lun > fConfig.max_lun) {
|
||||
ERROR("invalid lun device\n");
|
||||
fRequest->SetStatus(SCSI_LUN_INVALID);
|
||||
fRequest->Finish(false);
|
||||
return B_BAD_INDEX;
|
||||
}
|
||||
|
||||
if (ccb->cdb_length > VIRTIO_SCSI_CDB_SIZE) {
|
||||
fRequest->SetStatus(SCSI_REQ_INVALID);
|
||||
fRequest->Finish(false);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
bool isOut = (ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_OUT;
|
||||
bool isIn = (ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_IN;
|
||||
|
||||
// TODO check feature inout if request is bidirectional
|
||||
|
||||
fRequest->SetTimeout(ccb->timeout > 0 ? ccb->timeout * 1000 * 1000
|
||||
: VIRTIO_SCSI_STANDARD_TIMEOUT);
|
||||
|
||||
uint32 inCount = (isIn ? ccb->sg_count : 0) + 1;
|
||||
uint32 outCount = (isOut ? ccb->sg_count : 0) + 1;
|
||||
physical_entry entries[inCount + outCount];
|
||||
fRequest->FillRequest(inCount, outCount, entries);
|
||||
|
||||
{
|
||||
InterruptsSpinLocker locker(fInterruptLock);
|
||||
fExpectsInterrupt = true;
|
||||
fInterruptCondition.Add(&fInterruptConditionEntry);
|
||||
}
|
||||
|
||||
fVirtio->queue_request_v(fRequestVirtioQueue, entries,
|
||||
outCount, inCount, VirtioSCSIController::RequestCallback, this);
|
||||
|
||||
result = fInterruptConditionEntry.Wait(B_RELATIVE_TIMEOUT,
|
||||
fRequest->Timeout());
|
||||
|
||||
{
|
||||
InterruptsSpinLocker locker(fInterruptLock);
|
||||
fExpectsInterrupt = false;
|
||||
}
|
||||
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
return fRequest->Finish(false);
|
||||
}
|
||||
|
||||
|
||||
uchar
|
||||
VirtioSCSIController::AbortRequest(scsi_ccb *request)
|
||||
{
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
uchar
|
||||
VirtioSCSIController::TerminateRequest(scsi_ccb *request)
|
||||
{
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtioSCSIController::Control(uint8 targetID, uint32 op, void *buffer,
|
||||
size_t length)
|
||||
{
|
||||
CALLED();
|
||||
return B_DEV_INVALID_IOCTL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtioSCSIController::RequestCallback(void* cookie)
|
||||
{
|
||||
CALLED();
|
||||
VirtioSCSIController* controller = (VirtioSCSIController*)cookie;
|
||||
controller->_Interrupt();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtioSCSIController::_Interrupt()
|
||||
{
|
||||
SpinLocker locker(fInterruptLock);
|
||||
fInterruptCondition.NotifyAll();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2009, Michael Lotz, [email protected].
|
||||
* Copyright 2004-2007, Axel Dörfler, [email protected].
|
||||
* Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "VirtioSCSIPrivate.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
|
||||
|
||||
/*! Copy data between ccb data and buffer
|
||||
ccb - ccb to copy data from/to
|
||||
offset - offset of data in ccb
|
||||
allocation_length- limit of ccb's data buffer according to CDB
|
||||
buffer - data to copy data from/to
|
||||
size - number of bytes to copy
|
||||
to_buffer - true: copy from ccb to buffer
|
||||
false: copy from buffer to ccb
|
||||
return: true, if data of ccb was large enough
|
||||
*/
|
||||
bool
|
||||
copy_sg_data(scsi_ccb *ccb, uint offset, uint allocationLength,
|
||||
void *buffer, int size, bool toBuffer)
|
||||
{
|
||||
const physical_entry *sgList = ccb->sg_list;
|
||||
int sgCount = ccb->sg_count;
|
||||
|
||||
// skip unused S/G entries
|
||||
while (sgCount > 0 && offset >= sgList->size) {
|
||||
offset -= sgList->size;
|
||||
++sgList;
|
||||
--sgCount;
|
||||
}
|
||||
|
||||
if (sgCount == 0)
|
||||
return false;
|
||||
|
||||
// remaining bytes we are allowed to copy from/to ccb
|
||||
int requestSize = MIN(allocationLength, ccb->data_length) - offset;
|
||||
|
||||
// copy one S/G entry at a time
|
||||
for (; size > 0 && requestSize > 0 && sgCount > 0; ++sgList, --sgCount) {
|
||||
size_t bytes;
|
||||
|
||||
bytes = MIN(size, requestSize);
|
||||
bytes = MIN(bytes, sgList->size);
|
||||
|
||||
if (toBuffer) {
|
||||
vm_memcpy_from_physical(buffer, sgList->address + offset, bytes,
|
||||
false);
|
||||
} else {
|
||||
vm_memcpy_to_physical(sgList->address + offset, buffer, bytes,
|
||||
false);
|
||||
}
|
||||
|
||||
buffer = (char *)buffer + bytes;
|
||||
size -= bytes;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
swap_words(void *data, size_t size)
|
||||
{
|
||||
uint16 *word = (uint16 *)data;
|
||||
size_t count = size / 2;
|
||||
while (count--) {
|
||||
*word = B_BENDIAN_TO_HOST_INT16(*word);
|
||||
word++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2013, Jérôme Duval, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef VIRTIO_SCSI_PRIVATE_H
|
||||
#define VIRTIO_SCSI_PRIVATE_H
|
||||
|
||||
|
||||
#include <bus/SCSI.h>
|
||||
#include <condition_variable.h>
|
||||
#include <lock.h>
|
||||
#include <scsi_cmds.h>
|
||||
#include <virtio.h>
|
||||
|
||||
#include "virtio_scsi.h"
|
||||
|
||||
|
||||
//#define TRACE_VIRTIO_SCSI
|
||||
#ifdef TRACE_VIRTIO_SCSI
|
||||
# define TRACE(x...) dprintf("virtio_scsi: " x)
|
||||
#else
|
||||
# define TRACE(x...) ;
|
||||
#endif
|
||||
#define ERROR(x...) dprintf("\33[33mvirtio_scsi:\33[0m " x)
|
||||
#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
|
||||
|
||||
extern device_manager_info* gDeviceManager;
|
||||
extern scsi_for_sim_interface *gSCSI;
|
||||
|
||||
bool copy_sg_data(scsi_ccb *ccb, uint offset, uint allocationLength,
|
||||
void *buffer, int size, bool toBuffer);
|
||||
void swap_words(void *data, size_t size);
|
||||
|
||||
|
||||
#define VIRTIO_SCSI_STANDARD_TIMEOUT 10 * 1000 * 1000
|
||||
#define VIRTIO_SCSI_INITIATOR_ID 7
|
||||
|
||||
|
||||
class VirtioSCSIRequest;
|
||||
|
||||
|
||||
class VirtioSCSIController {
|
||||
public:
|
||||
VirtioSCSIController(device_node* node);
|
||||
~VirtioSCSIController();
|
||||
|
||||
status_t InitCheck();
|
||||
|
||||
void SetBus(scsi_bus bus);
|
||||
scsi_bus Bus() const { return fBus; }
|
||||
|
||||
void PathInquiry(scsi_path_inquiry* info);
|
||||
void GetRestrictions(uint8 targetID, bool* isATAPI,
|
||||
bool* noAutoSense, uint32* maxBlocks);
|
||||
uchar ResetDevice(uchar targetID, uchar targetLUN);
|
||||
status_t ExecuteRequest(scsi_ccb* request);
|
||||
uchar AbortRequest(scsi_ccb* request);
|
||||
uchar TerminateRequest(scsi_ccb* request);
|
||||
status_t Control(uint8 targetID, uint32 op,
|
||||
void* buffer, size_t length);
|
||||
|
||||
private:
|
||||
static void RequestCallback(void *cookie);
|
||||
void _Interrupt();
|
||||
|
||||
device_node* fNode;
|
||||
scsi_bus fBus;
|
||||
|
||||
virtio_device_interface* fVirtio;
|
||||
virtio_device* fVirtioDevice;
|
||||
|
||||
status_t fStatus;
|
||||
struct virtio_scsi_config fConfig;
|
||||
uint32 fFeatures;
|
||||
::virtio_queue fControlVirtioQueue;
|
||||
::virtio_queue fEventVirtioQueue;
|
||||
::virtio_queue fRequestVirtioQueue;
|
||||
|
||||
area_id fArea;
|
||||
struct virtio_scsi_event* fEvents;
|
||||
|
||||
VirtioSCSIRequest* fRequest;
|
||||
|
||||
spinlock fInterruptLock;
|
||||
ConditionVariable fInterruptCondition;
|
||||
ConditionVariableEntry fInterruptConditionEntry;
|
||||
bool fExpectsInterrupt;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class VirtioSCSIRequest {
|
||||
public:
|
||||
VirtioSCSIRequest(bool hasLock);
|
||||
~VirtioSCSIRequest();
|
||||
|
||||
void SetStatus(uint8 status);
|
||||
uint8 Status() const { return fStatus; }
|
||||
|
||||
void SetTimeout(bigtime_t timeout);
|
||||
bigtime_t Timeout() const { return fTimeout; }
|
||||
|
||||
bool HasSense() {
|
||||
return (fResponse->sense_len > 0); }
|
||||
|
||||
void SetIsWrite(bool isWrite);
|
||||
bool IsWrite() const { return fIsWrite; }
|
||||
|
||||
void SetBytesLeft(uint32 bytesLeft);
|
||||
size_t* BytesLeft() { return &fBytesLeft; }
|
||||
|
||||
bool HasData() const
|
||||
{ return fCCB->data_length > 0; }
|
||||
|
||||
status_t Finish(bool resubmit);
|
||||
|
||||
// SCSI stuff
|
||||
status_t Start(scsi_ccb *ccb);
|
||||
scsi_ccb* CCB() { return fCCB; }
|
||||
|
||||
void RequestSense();
|
||||
|
||||
void FillRequest(uint32 inCount, uint32 outCount,
|
||||
physical_entry *entries);
|
||||
|
||||
private:
|
||||
void _FillSense(scsi_sense *sense);
|
||||
uchar _ResponseStatus();
|
||||
|
||||
mutex fLock;
|
||||
bool fHasLock;
|
||||
|
||||
uint8 fStatus;
|
||||
|
||||
bigtime_t fTimeout;
|
||||
size_t fBytesLeft;
|
||||
bool fIsWrite;
|
||||
scsi_ccb* fCCB;
|
||||
|
||||
// virtio scsi
|
||||
void* fBuffer;
|
||||
struct virtio_scsi_cmd_req *fRequest;
|
||||
struct virtio_scsi_cmd_resp *fResponse;
|
||||
};
|
||||
|
||||
|
||||
#endif // VIRTIO_SCSI_PRIVATE_H
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright 2013, Jérôme Duval, [email protected].
|
||||
* Copyright 2009, Michael Lotz, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "VirtioSCSIPrivate.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
VirtioSCSIRequest::VirtioSCSIRequest(bool hasLock)
|
||||
:
|
||||
fHasLock(hasLock),
|
||||
fTimeout(0),
|
||||
fBytesLeft(0),
|
||||
fIsWrite(false),
|
||||
fCCB(NULL)
|
||||
{
|
||||
if (hasLock)
|
||||
mutex_init(&fLock, "virtio scsi request");
|
||||
|
||||
fBuffer = malloc(sizeof(struct virtio_scsi_cmd_req)
|
||||
+ sizeof(struct virtio_scsi_cmd_resp));
|
||||
bzero(fBuffer, sizeof(struct virtio_scsi_cmd_req)
|
||||
+ sizeof(struct virtio_scsi_cmd_resp));
|
||||
|
||||
fRequest = (struct virtio_scsi_cmd_req *)fBuffer;
|
||||
fResponse = (struct virtio_scsi_cmd_resp *)
|
||||
((addr_t)fBuffer + sizeof(struct virtio_scsi_cmd_req));
|
||||
|
||||
fResponse->sense_len = 0;
|
||||
}
|
||||
|
||||
|
||||
VirtioSCSIRequest::~VirtioSCSIRequest()
|
||||
{
|
||||
if (fHasLock)
|
||||
mutex_destroy(&fLock);
|
||||
|
||||
free(fBuffer);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtioSCSIRequest::SetStatus(uint8 status)
|
||||
{
|
||||
fStatus = status;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtioSCSIRequest::SetTimeout(bigtime_t timeout)
|
||||
{
|
||||
fTimeout = timeout;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtioSCSIRequest::SetIsWrite(bool isWrite)
|
||||
{
|
||||
fIsWrite = isWrite;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtioSCSIRequest::SetBytesLeft(uint32 bytesLeft)
|
||||
{
|
||||
fBytesLeft = bytesLeft;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtioSCSIRequest::Start(scsi_ccb *ccb)
|
||||
{
|
||||
CALLED();
|
||||
if (mutex_trylock(&fLock) != B_OK)
|
||||
return B_BUSY;
|
||||
|
||||
fCCB = ccb;
|
||||
fStatus = SCSI_REQ_CMP;
|
||||
fCCB->device_status = SCSI_STATUS_GOOD;
|
||||
fIsWrite = false;
|
||||
bzero(fResponse, sizeof(struct virtio_scsi_cmd_resp));
|
||||
|
||||
TRACE("VirtioSCSIRequest::Start() opcode %x tid %x lun %x\n", ccb->cdb[0],
|
||||
ccb->target_id, ccb->target_lun);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtioSCSIRequest::Finish(bool resubmit)
|
||||
{
|
||||
CALLED();
|
||||
fStatus = _ResponseStatus();
|
||||
fCCB->data_resid = fResponse->resid;
|
||||
fCCB->subsys_status = fStatus;
|
||||
|
||||
TRACE("VirtioSCSIRequest::Finish() status 0x%x response 0x%x resid:0x%x"
|
||||
" sense_len:%x\n", fResponse->status, fResponse->response,
|
||||
fResponse->resid, fResponse->sense_len);
|
||||
|
||||
if (fCCB->cdb[0] == SCSI_OP_INQUIRY) {
|
||||
// when the request is an inquiry, don't do anything
|
||||
} else if (fStatus == SCSI_REQ_CMP && fResponse->status != 0
|
||||
&& HasSense()) {
|
||||
// when the request completed and has set sense
|
||||
// data, report this to the scsi stack by setting
|
||||
// CHECK CONDITION status
|
||||
TRACE("setting check condition\n");
|
||||
|
||||
fCCB->subsys_status = SCSI_REQ_CMP_ERR;
|
||||
fCCB->device_status = SCSI_STATUS_CHECK_CONDITION;
|
||||
|
||||
// copy sense data if caller requested it
|
||||
if ((fCCB->flags & SCSI_DIS_AUTOSENSE) == 0) {
|
||||
size_t senseLength = min_c(sizeof(fCCB->sense),
|
||||
fResponse->sense_len);
|
||||
memcpy(fCCB->sense, fResponse->sense, senseLength);
|
||||
fCCB->sense_resid = sizeof(fCCB->sense) - senseLength;
|
||||
fCCB->subsys_status |= SCSI_AUTOSNS_VALID;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&fLock);
|
||||
|
||||
if (resubmit)
|
||||
gSCSI->resubmit(fCCB);
|
||||
else
|
||||
gSCSI->finished(fCCB, 1);
|
||||
|
||||
TRACE("VirtioSCSIRequest::Finish() done\n");
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtioSCSIRequest::RequestSense()
|
||||
{
|
||||
CALLED();
|
||||
// Copy sense data from last request into data buffer of current request.
|
||||
// The sense data of last request is still present in the current request,
|
||||
// as it isn't cleared on SCSI_OP_REQUEST_SENSE.
|
||||
scsi_cmd_request_sense *command = (scsi_cmd_request_sense *)fCCB->cdb;
|
||||
copy_sg_data(fCCB, 0, command->allocation_length, fResponse->sense,
|
||||
fResponse->sense_len, false);
|
||||
|
||||
fCCB->data_resid = fCCB->data_length - min_c(min_c(fResponse->sense_len,
|
||||
command->allocation_length), fCCB->data_length);
|
||||
fResponse->sense_len = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtioSCSIRequest::FillRequest(uint32 inCount, uint32 outCount,
|
||||
physical_entry *entries)
|
||||
{
|
||||
CALLED();
|
||||
fRequest->task_attr = VIRTIO_SCSI_S_SIMPLE;
|
||||
fRequest->tag = (addr_t)fCCB;
|
||||
fRequest->lun[0] = 1;
|
||||
fRequest->lun[1] = fCCB->target_id;
|
||||
fRequest->lun[2] = 0x40 | ((fCCB->target_lun >> 8) & 0x3f);
|
||||
fRequest->lun[3] = (fCCB->target_lun >> 8) & 0xff;
|
||||
|
||||
memcpy(fRequest->cdb, fCCB->cdb, min_c(fCCB->cdb_length,
|
||||
sizeof(fRequest->cdb)));
|
||||
|
||||
get_memory_map(fBuffer, sizeof(struct virtio_scsi_cmd_req)
|
||||
+ sizeof(struct virtio_scsi_cmd_resp), &entries[0], 1);
|
||||
entries[0].size = sizeof(struct virtio_scsi_cmd_req);
|
||||
if (outCount > 1) {
|
||||
memcpy(entries + 1, fCCB->sg_list, fCCB->sg_count
|
||||
* sizeof(physical_entry));
|
||||
}
|
||||
|
||||
entries[outCount].address = entries[0].address
|
||||
+ sizeof(struct virtio_scsi_cmd_req);
|
||||
entries[outCount].size = sizeof(struct virtio_scsi_cmd_resp);
|
||||
|
||||
if (inCount > 1) {
|
||||
memcpy(entries + outCount + 1, fCCB->sg_list, fCCB->sg_count
|
||||
* sizeof(physical_entry));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uchar
|
||||
VirtioSCSIRequest::_ResponseStatus()
|
||||
{
|
||||
uchar status;
|
||||
|
||||
switch (fResponse->response) {
|
||||
case VIRTIO_SCSI_S_OK:
|
||||
status = SCSI_REQ_CMP;
|
||||
break;
|
||||
case VIRTIO_SCSI_S_OVERRUN:
|
||||
status = SCSI_DATA_RUN_ERR;
|
||||
break;
|
||||
case VIRTIO_SCSI_S_ABORTED:
|
||||
status = SCSI_REQ_ABORTED;
|
||||
break;
|
||||
case VIRTIO_SCSI_S_BAD_TARGET:
|
||||
status = SCSI_TID_INVALID;
|
||||
break;
|
||||
case VIRTIO_SCSI_S_RESET:
|
||||
status = SCSI_SCSI_BUS_RESET;
|
||||
break;
|
||||
case VIRTIO_SCSI_S_BUSY:
|
||||
status = SCSI_SCSI_BUSY;
|
||||
break;
|
||||
case VIRTIO_SCSI_S_TRANSPORT_FAILURE:
|
||||
case VIRTIO_SCSI_S_TARGET_FAILURE:
|
||||
case VIRTIO_SCSI_S_NEXUS_FAILURE:
|
||||
status = SCSI_NO_NEXUS;
|
||||
break;
|
||||
default: /* VIRTIO_SCSI_S_FAILURE */
|
||||
status = SCSI_REQ_CMP_ERR;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright 2013, Jérôme Duval, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "VirtioSCSIPrivate.h"
|
||||
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define VIRTIO_SCSI_ID_GENERATOR "virtio_scsi/id"
|
||||
#define VIRTIO_SCSI_ID_ITEM "virtio_scsi/id"
|
||||
#define VIRTIO_SCSI_BRIDGE_PRETTY_NAME "Virtio SCSI Bridge"
|
||||
#define VIRTIO_SCSI_CONTROLLER_PRETTY_NAME "Virtio SCSI Controller"
|
||||
|
||||
#define VIRTIO_SCSI_DEVICE_MODULE_NAME "busses/scsi/virtio_scsi/driver_v1"
|
||||
#define VIRTIO_SCSI_SIM_MODULE_NAME "busses/scsi/virtio_scsi/sim/driver_v1"
|
||||
|
||||
|
||||
device_manager_info *gDeviceManager;
|
||||
scsi_for_sim_interface *gSCSI;
|
||||
|
||||
|
||||
// #pragma mark - SIM module interface
|
||||
|
||||
|
||||
static void
|
||||
set_scsi_bus(scsi_sim_cookie cookie, scsi_bus bus)
|
||||
{
|
||||
VirtioSCSIController *sim = (VirtioSCSIController *)cookie;
|
||||
sim->SetBus(bus);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
scsi_io(scsi_sim_cookie cookie, scsi_ccb *request)
|
||||
{
|
||||
CALLED();
|
||||
VirtioSCSIController *sim = (VirtioSCSIController *)cookie;
|
||||
if (sim->ExecuteRequest(request) == B_BUSY)
|
||||
gSCSI->requeue(request, true);
|
||||
}
|
||||
|
||||
|
||||
static uchar
|
||||
abort_io(scsi_sim_cookie cookie, scsi_ccb *request)
|
||||
{
|
||||
CALLED();
|
||||
VirtioSCSIController *sim = (VirtioSCSIController *)cookie;
|
||||
return sim->AbortRequest(request);
|
||||
}
|
||||
|
||||
|
||||
static uchar
|
||||
reset_device(scsi_sim_cookie cookie, uchar targetID, uchar targetLUN)
|
||||
{
|
||||
CALLED();
|
||||
VirtioSCSIController *sim = (VirtioSCSIController *)cookie;
|
||||
return sim->ResetDevice(targetID, targetLUN);
|
||||
}
|
||||
|
||||
|
||||
static uchar
|
||||
terminate_io(scsi_sim_cookie cookie, scsi_ccb *request)
|
||||
{
|
||||
CALLED();
|
||||
VirtioSCSIController *sim = (VirtioSCSIController *)cookie;
|
||||
return sim->TerminateRequest(request);
|
||||
}
|
||||
|
||||
|
||||
static uchar
|
||||
path_inquiry(scsi_sim_cookie cookie, scsi_path_inquiry *info)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
VirtioSCSIController *sim = (VirtioSCSIController *)cookie;
|
||||
if (sim->Bus() == NULL)
|
||||
return SCSI_NO_HBA;
|
||||
|
||||
sim->PathInquiry(info);
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
//! this is called immediately before the SCSI bus manager scans the bus
|
||||
static uchar
|
||||
scan_bus(scsi_sim_cookie cookie)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
static uchar
|
||||
reset_bus(scsi_sim_cookie cookie)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
/*! Get restrictions of one device
|
||||
(used for non-SCSI transport protocols and bug fixes)
|
||||
*/
|
||||
static void
|
||||
get_restrictions(scsi_sim_cookie cookie, uchar targetID, bool *isATAPI,
|
||||
bool *noAutoSense, uint32 *maxBlocks)
|
||||
{
|
||||
CALLED();
|
||||
VirtioSCSIController *sim = (VirtioSCSIController *)cookie;
|
||||
sim->GetRestrictions(targetID, isATAPI, noAutoSense, maxBlocks);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ioctl(scsi_sim_cookie cookie, uint8 targetID, uint32 op, void *buffer,
|
||||
size_t length)
|
||||
{
|
||||
CALLED();
|
||||
VirtioSCSIController *sim = (VirtioSCSIController *)cookie;
|
||||
return sim->Control(targetID, op, buffer, length);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
sim_init_bus(device_node *node, void **_cookie)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
VirtioSCSIController *controller = new(std::nothrow)
|
||||
VirtioSCSIController(node);
|
||||
if (controller == NULL)
|
||||
return B_NO_MEMORY;
|
||||
status_t status = controller->InitCheck();
|
||||
if (status < B_OK) {
|
||||
delete controller;
|
||||
return status;
|
||||
}
|
||||
|
||||
*_cookie = controller;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sim_uninit_bus(void *cookie)
|
||||
{
|
||||
CALLED();
|
||||
VirtioSCSIController *controller = (VirtioSCSIController*)cookie;
|
||||
|
||||
delete controller;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static float
|
||||
virtio_scsi_supports_device(device_node *parent)
|
||||
{
|
||||
const char *bus;
|
||||
uint16 deviceType;
|
||||
|
||||
// make sure parent is really the Virtio bus manager
|
||||
if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
|
||||
return -1;
|
||||
|
||||
if (strcmp(bus, "virtio"))
|
||||
return 0.0;
|
||||
|
||||
// check whether it's really a Virtio SCSI Device
|
||||
if (gDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM,
|
||||
&deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_SCSI)
|
||||
return 0.0;
|
||||
|
||||
TRACE("Virtio SCSI device found!\n");
|
||||
|
||||
return 0.6f;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
virtio_scsi_register_device(device_node *parent)
|
||||
{
|
||||
CALLED();
|
||||
virtio_device_interface* virtio = NULL;
|
||||
virtio_device* virtioDevice = NULL;
|
||||
struct virtio_scsi_config config;
|
||||
|
||||
gDeviceManager->get_driver(parent, (driver_module_info **)&virtio,
|
||||
(void **)&virtioDevice);
|
||||
|
||||
status_t status = virtio->read_device_config(virtioDevice, 0, &config,
|
||||
sizeof(struct virtio_scsi_config));
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
uint32 max_targets = config.max_target + 1;
|
||||
uint32 max_blocks = 0x10000;
|
||||
if (config.max_sectors != 0)
|
||||
max_blocks = config.max_sectors;
|
||||
|
||||
device_attr attrs[] = {
|
||||
{ SCSI_DEVICE_MAX_TARGET_COUNT, B_UINT32_TYPE,
|
||||
{ ui32: max_targets }},
|
||||
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
|
||||
{ string: VIRTIO_SCSI_BRIDGE_PRETTY_NAME }},
|
||||
|
||||
// DMA properties
|
||||
{ B_DMA_MAX_SEGMENT_BLOCKS, B_UINT32_TYPE, { ui32: max_blocks }},
|
||||
{ B_DMA_MAX_SEGMENT_COUNT, B_UINT32_TYPE,
|
||||
{ ui32: config.seg_max }},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
return gDeviceManager->register_node(parent, VIRTIO_SCSI_DEVICE_MODULE_NAME,
|
||||
attrs, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
virtio_scsi_init_driver(device_node *node, void **_cookie)
|
||||
{
|
||||
CALLED();
|
||||
*_cookie = node;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
virtio_scsi_register_child_devices(void *cookie)
|
||||
{
|
||||
CALLED();
|
||||
device_node *node = (device_node *)cookie;
|
||||
|
||||
int32 id = gDeviceManager->create_id(VIRTIO_SCSI_ID_GENERATOR);
|
||||
if (id < 0)
|
||||
return id;
|
||||
|
||||
device_attr attrs[] = {
|
||||
{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
|
||||
{ string: SCSI_FOR_SIM_MODULE_NAME }},
|
||||
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
|
||||
{ string: VIRTIO_SCSI_CONTROLLER_PRETTY_NAME }},
|
||||
{ SCSI_DESCRIPTION_CONTROLLER_NAME, B_STRING_TYPE,
|
||||
{ string: VIRTIO_SCSI_DEVICE_MODULE_NAME }},
|
||||
{ B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, { ui32: 255 }},
|
||||
{ VIRTIO_SCSI_ID_ITEM, B_UINT32_TYPE, { ui32: (uint32)id }},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
status_t status = gDeviceManager->register_node(node,
|
||||
VIRTIO_SCSI_SIM_MODULE_NAME, attrs, NULL, NULL);
|
||||
if (status < B_OK)
|
||||
gDeviceManager->free_id(VIRTIO_SCSI_ID_GENERATOR, id);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
case B_MODULE_UNINIT:
|
||||
return B_OK;
|
||||
|
||||
default:
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static scsi_sim_interface sVirtioSCSISimInterface = {
|
||||
{
|
||||
{
|
||||
VIRTIO_SCSI_SIM_MODULE_NAME,
|
||||
0,
|
||||
std_ops
|
||||
},
|
||||
NULL, // supported devices
|
||||
NULL, // register node
|
||||
sim_init_bus,
|
||||
sim_uninit_bus,
|
||||
NULL, // register child devices
|
||||
NULL, // rescan
|
||||
NULL // bus_removed
|
||||
},
|
||||
set_scsi_bus,
|
||||
scsi_io,
|
||||
abort_io,
|
||||
reset_device,
|
||||
terminate_io,
|
||||
path_inquiry,
|
||||
scan_bus,
|
||||
reset_bus,
|
||||
get_restrictions,
|
||||
ioctl
|
||||
};
|
||||
|
||||
|
||||
static driver_module_info sVirtioSCSIDevice = {
|
||||
{
|
||||
VIRTIO_SCSI_DEVICE_MODULE_NAME,
|
||||
0,
|
||||
std_ops
|
||||
},
|
||||
virtio_scsi_supports_device,
|
||||
virtio_scsi_register_device,
|
||||
virtio_scsi_init_driver,
|
||||
NULL, // uninit_driver,
|
||||
virtio_scsi_register_child_devices,
|
||||
NULL, // rescan
|
||||
NULL, // device_removed
|
||||
};
|
||||
|
||||
|
||||
module_dependency module_dependencies[] = {
|
||||
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager },
|
||||
{ SCSI_FOR_SIM_MODULE_NAME, (module_info **)&gSCSI },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
module_info *modules[] = {
|
||||
(module_info *)&sVirtioSCSIDevice,
|
||||
(module_info *)&sVirtioSCSISimInterface,
|
||||
NULL
|
||||
};
|
||||
@@ -0,0 +1,152 @@
|
||||
/*-
|
||||
* This header is BSD licensed so anyone can use the definitions to implement
|
||||
* compatible drivers/servers.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _VIRTIO_SCSI_H
|
||||
#define _VIRTIO_SCSI_H
|
||||
|
||||
/* Feature bits */
|
||||
#define VIRTIO_SCSI_F_INOUT 0x0001 /* Single request can contain both
|
||||
* read and write buffers */
|
||||
#define VIRTIO_SCSI_F_HOTPLUG 0x0002 /* Host should enable hot plug/unplug
|
||||
* of new LUNs and targets.
|
||||
*/
|
||||
|
||||
#define VIRTIO_SCSI_CDB_SIZE 32
|
||||
#define VIRTIO_SCSI_SENSE_SIZE 96
|
||||
|
||||
/* SCSI command request, followed by data-out */
|
||||
struct virtio_scsi_cmd_req {
|
||||
uint8_t lun[8]; /* Logical Unit Number */
|
||||
uint64_t tag; /* Command identifier */
|
||||
uint8_t task_attr; /* Task attribute */
|
||||
uint8_t prio;
|
||||
uint8_t crn;
|
||||
uint8_t cdb[VIRTIO_SCSI_CDB_SIZE];
|
||||
} _PACKED;
|
||||
|
||||
/* Response, followed by sense data and data-in */
|
||||
struct virtio_scsi_cmd_resp {
|
||||
uint32_t sense_len; /* Sense data length */
|
||||
uint32_t resid; /* Residual bytes in data buffer */
|
||||
uint16_t status_qualifier; /* Status qualifier */
|
||||
uint8_t status; /* Command completion status */
|
||||
uint8_t response; /* Response values */
|
||||
uint8_t sense[VIRTIO_SCSI_SENSE_SIZE];
|
||||
} _PACKED;
|
||||
|
||||
/* Task Management Request */
|
||||
struct virtio_scsi_ctrl_tmf_req {
|
||||
uint32_t type;
|
||||
uint32_t subtype;
|
||||
uint8_t lun[8];
|
||||
uint64_t tag;
|
||||
} _PACKED;
|
||||
|
||||
struct virtio_scsi_ctrl_tmf_resp {
|
||||
uint8_t response;
|
||||
} _PACKED;
|
||||
|
||||
/* Asynchronous notification query/subscription */
|
||||
struct virtio_scsi_ctrl_an_req {
|
||||
uint32_t type;
|
||||
uint8_t lun[8];
|
||||
uint32_t event_requested;
|
||||
} _PACKED;
|
||||
|
||||
struct virtio_scsi_ctrl_an_resp {
|
||||
uint32_t event_actual;
|
||||
uint8_t response;
|
||||
} _PACKED;
|
||||
|
||||
struct virtio_scsi_event {
|
||||
uint32_t event;
|
||||
uint8_t lun[8];
|
||||
uint32_t reason;
|
||||
} _PACKED;
|
||||
|
||||
struct virtio_scsi_config {
|
||||
uint32_t num_queues;
|
||||
uint32_t seg_max;
|
||||
uint32_t max_sectors;
|
||||
uint32_t cmd_per_lun;
|
||||
uint32_t event_info_size;
|
||||
uint32_t sense_size;
|
||||
uint32_t cdb_size;
|
||||
uint16_t max_channel;
|
||||
uint16_t max_target;
|
||||
uint32_t max_lun;
|
||||
} _PACKED;
|
||||
|
||||
/* Response codes */
|
||||
#define VIRTIO_SCSI_S_OK 0
|
||||
#define VIRTIO_SCSI_S_FUNCTION_COMPLETE 0
|
||||
#define VIRTIO_SCSI_S_OVERRUN 1
|
||||
#define VIRTIO_SCSI_S_ABORTED 2
|
||||
#define VIRTIO_SCSI_S_BAD_TARGET 3
|
||||
#define VIRTIO_SCSI_S_RESET 4
|
||||
#define VIRTIO_SCSI_S_BUSY 5
|
||||
#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6
|
||||
#define VIRTIO_SCSI_S_TARGET_FAILURE 7
|
||||
#define VIRTIO_SCSI_S_NEXUS_FAILURE 8
|
||||
#define VIRTIO_SCSI_S_FAILURE 9
|
||||
#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10
|
||||
#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11
|
||||
#define VIRTIO_SCSI_S_INCORRECT_LUN 12
|
||||
|
||||
/* Controlq type codes. */
|
||||
#define VIRTIO_SCSI_T_TMF 0
|
||||
#define VIRTIO_SCSI_T_AN_QUERY 1
|
||||
#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2
|
||||
|
||||
/* Valid TMF subtypes. */
|
||||
#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0
|
||||
#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1
|
||||
#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2
|
||||
#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3
|
||||
#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4
|
||||
#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5
|
||||
#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6
|
||||
#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7
|
||||
|
||||
/* Events. */
|
||||
#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000
|
||||
#define VIRTIO_SCSI_T_NO_EVENT 0
|
||||
#define VIRTIO_SCSI_T_TRANSPORT_RESET 1
|
||||
#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2
|
||||
|
||||
/* Reasons of transport reset event */
|
||||
#define VIRTIO_SCSI_EVT_RESET_HARD 0
|
||||
#define VIRTIO_SCSI_EVT_RESET_RESCAN 1
|
||||
#define VIRTIO_SCSI_EVT_RESET_REMOVED 2
|
||||
|
||||
#define VIRTIO_SCSI_S_SIMPLE 0
|
||||
#define VIRTIO_SCSI_S_ORDERED 1
|
||||
#define VIRTIO_SCSI_S_HEAD 2
|
||||
#define VIRTIO_SCSI_S_ACA 3
|
||||
|
||||
#endif /* _VIRTIO_SCSI_H */
|
||||
Reference in New Issue
Block a user