* Copyright 2013, JΓ©rΓ΄me Duval, korli@users.berlios.de.
* 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;
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;
}
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;
}
(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);
}
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;
}
static float
virtio_scsi_supports_device(device_node *parent)
{
const char *bus;
uint16 deviceType;
if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
return -1;
if (strcmp(bus, "virtio"))
return 0.0;
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_luns = config.max_lun + 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 }},
{ SCSI_DEVICE_MAX_LUN_COUNT, B_UINT32_TYPE,
{ .ui32 = max_luns }},
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
{ .string = VIRTIO_SCSI_BRIDGE_PRETTY_NAME }},
{ 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,
NULL,
sim_init_bus,
sim_uninit_bus,
NULL,
NULL,
NULL
},
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,
virtio_scsi_register_child_devices,
NULL,
NULL,
};
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
};