/* * Copyright 2013, Jérôme Duval, korli@users.berlios.de. * Distributed under the terms of the MIT License. */ #include "VirtioSCSIPrivate.h" #include #include #include #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_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 }}, // 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 };