virtio_scsi: now handles events like capacity data changed.
* push event requests on the event queue, handles them. * activate feature VIRTIO_SCSI_F_CHANGE. * when a capacity data changed event is received, schedule a rescan of the scsi device node on the scsi dpc queue. we find out unique child node and call the rescan_node() hook. * Haiku only handles media changed events on removable devices, so the feature only actually works when the device is defined as such, aka for QEMU the following option: -device scsi-hd,drive=hd,removable=true
This commit is contained in:
parent
ed4a8e4d11
commit
e598dee752
@ -32,13 +32,17 @@ 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);
|
||||
device_node *virtioParent = gDeviceManager->get_parent_node(parent);
|
||||
@ -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,
|
||||
@ -84,6 +88,9 @@ VirtioSCSIController::VirtioSCSIController(device_node *node)
|
||||
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) {
|
||||
ERROR("interrupt setup failed (%s)\n", strerror(fStatus));
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user