diff --git a/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIController.cpp b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIController.cpp index d66d2ce348..308835deff 100644 --- a/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIController.cpp +++ b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIController.cpp @@ -32,12 +32,16 @@ VirtioSCSIController::VirtioSCSIController(device_node *node) fVirtio(NULL), fVirtioDevice(NULL), fStatus(B_NO_INIT), - fRequest(NULL) + fRequest(NULL), + fEventDPC(NULL) { CALLED(); B_INITIALIZE_SPINLOCK(&fInterruptLock); fInterruptCondition.Init(this, "virtio scsi transfer"); + + if (gSCSI->alloc_dpc(&fEventDPC) != B_OK) + return; // get the Virtio device from our parent's parent device_node *parent = gDeviceManager->get_parent_node(node); @@ -49,7 +53,7 @@ VirtioSCSIController::VirtioSCSIController(device_node *node) gDeviceManager->put_node(virtioParent); fVirtio->negociate_features(fVirtioDevice, - 0 /*VIRTIO_SCSI_F_HOTPLUG*/, + VIRTIO_SCSI_F_CHANGE /*VIRTIO_SCSI_F_HOTPLUG*/, &fFeatures, &get_feature_name); fStatus = fVirtio->read_device_config(fVirtioDevice, 0, &fConfig, @@ -83,6 +87,9 @@ VirtioSCSIController::VirtioSCSIController(device_node *node) fControlVirtioQueue = virtioQueues[0]; fEventVirtioQueue = virtioQueues[1]; fRequestVirtioQueue = virtioQueues[2]; + + for (uint32 i = 0; i < VIRTIO_SCSI_NUM_EVENTS; i++) + _SubmitEvent(i); fStatus = fVirtio->setup_interrupt(fVirtioDevice, NULL, this); if (fStatus != B_OK) { @@ -98,6 +105,8 @@ VirtioSCSIController::~VirtioSCSIController() { CALLED(); delete fRequest; + + gSCSI->free_dpc(fEventDPC); } @@ -262,3 +271,81 @@ VirtioSCSIController::_RequestInterrupt() fInterruptCondition.NotifyAll(); } + + +void +VirtioSCSIController::_EventCallback(void* driverCookie, void* cookie) +{ + CALLED(); + VirtioSCSIController* controller = (VirtioSCSIController*)driverCookie; + struct virtio_scsi_event* event = (struct virtio_scsi_event*)cookie; + controller->_EventInterrupt(event); +} + + +void +VirtioSCSIController::_EventInterrupt(struct virtio_scsi_event* event) +{ + CALLED(); + TRACE("events %#x\n", event->event); + if ((event->event & VIRTIO_SCSI_T_EVENTS_MISSED) != 0) { + ERROR("events missed\n"); + } else switch (event->event) { + case VIRTIO_SCSI_T_TRANSPORT_RESET: + ERROR("transport reset\n"); + break; + case VIRTIO_SCSI_T_ASYNC_NOTIFY: + ERROR("async notify\n"); + break; + case VIRTIO_SCSI_T_PARAM_CHANGE: + { + uint16 sense = (event->reason >> 8) + | ((event->reason & 0xff) << 8); + if (sense == SCSIS_ASC_CAPACITY_DATA_HAS_CHANGED) { + ERROR("capacity data has changed for %x:%x\n", event->lun[1], + event->lun[2] << 8 | event->lun[3]); + gSCSI->schedule_dpc(fBus, fEventDPC, _RescanChildBus, this); + } else + ERROR("param change, unknown reason\n"); + break; + } + default: + ERROR("unknown event %#x\n", event->event); + break; + } +} + + +void +VirtioSCSIController::_SubmitEvent(uint32 eventNumber) +{ + CALLED(); + struct virtio_scsi_event* event = &fEventBuffers[eventNumber]; + bzero(event, sizeof(struct virtio_scsi_event)); + + physical_entry entry; + get_memory_map(event, sizeof(struct virtio_scsi_event), &entry, 1); + + fVirtio->queue_request_v(fEventVirtioQueue, &entry, + 0, 1, VirtioSCSIController::_EventCallback, event); +} + + +void +VirtioSCSIController::_RescanChildBus(void *cookie) +{ + CALLED(); + VirtioSCSIController* controller = (VirtioSCSIController*)cookie; + device_node *childNode = NULL; + const device_attr attrs[] = { { NULL } }; + if (gDeviceManager->get_next_child_node(controller->fNode, attrs, + &childNode) != B_OK) { + ERROR("couldn't find the child node for %p\n", controller->fNode); + return; + } + + gDeviceManager->rescan_node(childNode); + TRACE("rescan done %p\n", childNode); + gDeviceManager->put_node(childNode); +} + diff --git a/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIPrivate.h b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIPrivate.h index 8e48c01a9d..ca810deab2 100644 --- a/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIPrivate.h +++ b/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIPrivate.h @@ -35,6 +35,8 @@ void swap_words(void *data, size_t size); #define VIRTIO_SCSI_STANDARD_TIMEOUT 10 * 1000 * 1000 #define VIRTIO_SCSI_INITIATOR_ID 7 +#define VIRTIO_SCSI_NUM_EVENTS 4 + class VirtioSCSIRequest; @@ -63,6 +65,11 @@ private: static void _RequestCallback(void* driverCookie, void *cookie); void _RequestInterrupt(); + static void _EventCallback(void *driverCookie, void *cookie); + void _EventInterrupt(struct virtio_scsi_event* event); + static void _RescanChildBus(void *cookie); + + void _SubmitEvent(uint32 event); device_node* fNode; scsi_bus fBus; @@ -87,6 +94,8 @@ private: ConditionVariableEntry fInterruptConditionEntry; bool fExpectsInterrupt; + scsi_dpc_cookie fEventDPC; + struct virtio_scsi_event fEventBuffers[VIRTIO_SCSI_NUM_EVENTS]; };