2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
3 * Distributed under the terms of the MIT License.
7 #include "VirtioSCSIPrivate.h"
13 #include <util/AutoLock.h>
17 get_feature_name(uint32 feature
)
20 case VIRTIO_SCSI_F_INOUT
:
22 case VIRTIO_SCSI_F_HOTPLUG
:
24 case VIRTIO_SCSI_F_CHANGE
:
31 VirtioSCSIController::VirtioSCSIController(device_node
*node
)
42 B_INITIALIZE_SPINLOCK(&fInterruptLock
);
43 fInterruptCondition
.Init(this, "virtio scsi transfer");
45 if (gSCSI
->alloc_dpc(&fEventDPC
) != B_OK
)
48 // get the Virtio device from our parent's parent
49 device_node
*parent
= gDeviceManager
->get_parent_node(node
);
50 device_node
*virtioParent
= gDeviceManager
->get_parent_node(parent
);
51 gDeviceManager
->put_node(parent
);
53 gDeviceManager
->get_driver(virtioParent
, (driver_module_info
**)&fVirtio
,
54 (void **)&fVirtioDevice
);
55 gDeviceManager
->put_node(virtioParent
);
57 fVirtio
->negociate_features(fVirtioDevice
,
58 VIRTIO_SCSI_F_CHANGE
/*VIRTIO_SCSI_F_HOTPLUG*/,
59 &fFeatures
, &get_feature_name
);
61 fStatus
= fVirtio
->read_device_config(fVirtioDevice
, 0, &fConfig
,
62 sizeof(struct virtio_scsi_config
));
66 fConfig
.sense_size
= VIRTIO_SCSI_SENSE_SIZE
;
67 fConfig
.cdb_size
= VIRTIO_SCSI_CDB_SIZE
;
69 fVirtio
->write_device_config(fVirtioDevice
,
70 offsetof(struct virtio_scsi_config
, sense_size
), &fConfig
.sense_size
,
71 sizeof(fConfig
.sense_size
));
72 fVirtio
->write_device_config(fVirtioDevice
,
73 offsetof(struct virtio_scsi_config
, cdb_size
), &fConfig
.sense_size
,
74 sizeof(fConfig
.cdb_size
));
76 fRequest
= new(std::nothrow
) VirtioSCSIRequest(true);
77 if (fRequest
== NULL
) {
78 fStatus
= B_NO_MEMORY
;
82 ::virtio_queue virtioQueues
[3];
83 fStatus
= fVirtio
->alloc_queues(fVirtioDevice
, 3, virtioQueues
);
84 if (fStatus
!= B_OK
) {
85 ERROR("queue allocation failed (%s)\n", strerror(fStatus
));
89 fControlVirtioQueue
= virtioQueues
[0];
90 fEventVirtioQueue
= virtioQueues
[1];
91 fRequestVirtioQueue
= virtioQueues
[2];
93 for (uint32 i
= 0; i
< VIRTIO_SCSI_NUM_EVENTS
; i
++)
96 fStatus
= fVirtio
->setup_interrupt(fVirtioDevice
, NULL
, this);
97 if (fStatus
!= B_OK
) {
98 ERROR("interrupt setup failed (%s)\n", strerror(fStatus
));
106 VirtioSCSIController::~VirtioSCSIController()
111 gSCSI
->free_dpc(fEventDPC
);
116 VirtioSCSIController::InitCheck()
123 VirtioSCSIController::SetBus(scsi_bus bus
)
130 VirtioSCSIController::PathInquiry(scsi_path_inquiry
*info
)
132 info
->hba_inquiry
= SCSI_PI_TAG_ABLE
;
135 info
->initiator_id
= VIRTIO_SCSI_INITIATOR_ID
;
136 info
->hba_queue_size
= fConfig
.cmd_per_lun
!= 0 ? fConfig
.cmd_per_lun
: 1;
137 memset(info
->vuhba_flags
, 0, sizeof(info
->vuhba_flags
));
139 strlcpy(info
->sim_vid
, "Haiku", SCSI_SIM_ID
);
140 strlcpy(info
->hba_vid
, "VirtIO", SCSI_HBA_ID
);
142 strlcpy(info
->sim_version
, "1.0", SCSI_VERS
);
143 strlcpy(info
->hba_version
, "1.0", SCSI_VERS
);
144 strlcpy(info
->controller_family
, "Virtio", SCSI_FAM_ID
);
145 strlcpy(info
->controller_type
, "Virtio", SCSI_TYPE_ID
);
150 VirtioSCSIController::GetRestrictions(uint8 targetID
, bool *isATAPI
,
151 bool *noAutoSense
, uint32
*maxBlocks
)
155 *maxBlocks
= fConfig
.cmd_per_lun
;
160 VirtioSCSIController::ResetDevice(uchar targetID
, uchar targetLUN
)
167 VirtioSCSIController::ExecuteRequest(scsi_ccb
*ccb
)
169 status_t result
= fRequest
->Start(ccb
);
173 if (ccb
->cdb
[0] == SCSI_OP_REQUEST_SENSE
&& fRequest
->HasSense()) {
174 TRACE("request sense\n");
175 fRequest
->RequestSense();
176 fRequest
->Finish(false);
180 if (ccb
->target_id
> fConfig
.max_target
) {
181 ERROR("invalid target device\n");
182 fRequest
->SetStatus(SCSI_TID_INVALID
);
183 fRequest
->Finish(false);
187 if (ccb
->target_lun
> fConfig
.max_lun
) {
188 ERROR("invalid lun device\n");
189 fRequest
->SetStatus(SCSI_LUN_INVALID
);
190 fRequest
->Finish(false);
194 if (ccb
->cdb_length
> VIRTIO_SCSI_CDB_SIZE
) {
195 fRequest
->SetStatus(SCSI_REQ_INVALID
);
196 fRequest
->Finish(false);
200 bool isOut
= (ccb
->flags
& SCSI_DIR_MASK
) == SCSI_DIR_OUT
;
201 bool isIn
= (ccb
->flags
& SCSI_DIR_MASK
) == SCSI_DIR_IN
;
203 // TODO check feature inout if request is bidirectional
205 fRequest
->SetTimeout(ccb
->timeout
> 0 ? ccb
->timeout
* 1000 * 1000
206 : VIRTIO_SCSI_STANDARD_TIMEOUT
);
208 uint32 inCount
= (isIn
? ccb
->sg_count
: 0) + 1;
209 uint32 outCount
= (isOut
? ccb
->sg_count
: 0) + 1;
210 physical_entry entries
[inCount
+ outCount
];
211 fRequest
->FillRequest(inCount
, outCount
, entries
);
214 InterruptsSpinLocker
locker(fInterruptLock
);
215 fExpectsInterrupt
= true;
216 fInterruptCondition
.Add(&fInterruptConditionEntry
);
219 fVirtio
->queue_request_v(fRequestVirtioQueue
, entries
,
220 outCount
, inCount
, VirtioSCSIController::_RequestCallback
, NULL
);
222 result
= fInterruptConditionEntry
.Wait(B_RELATIVE_TIMEOUT
,
223 fRequest
->Timeout());
226 InterruptsSpinLocker
locker(fInterruptLock
);
227 fExpectsInterrupt
= false;
233 return fRequest
->Finish(false);
238 VirtioSCSIController::AbortRequest(scsi_ccb
*request
)
245 VirtioSCSIController::TerminateRequest(scsi_ccb
*request
)
252 VirtioSCSIController::Control(uint8 targetID
, uint32 op
, void *buffer
,
256 return B_DEV_INVALID_IOCTL
;
261 VirtioSCSIController::_RequestCallback(void* driverCookie
, void* cookie
)
264 VirtioSCSIController
* controller
= (VirtioSCSIController
*)driverCookie
;
265 controller
->_RequestInterrupt();
270 VirtioSCSIController::_RequestInterrupt()
272 SpinLocker
locker(fInterruptLock
);
273 fInterruptCondition
.NotifyAll();
279 VirtioSCSIController::_EventCallback(void* driverCookie
, void* cookie
)
282 VirtioSCSIController
* controller
= (VirtioSCSIController
*)driverCookie
;
283 struct virtio_scsi_event
* event
= (struct virtio_scsi_event
*)cookie
;
284 controller
->_EventInterrupt(event
);
289 VirtioSCSIController::_EventInterrupt(struct virtio_scsi_event
* event
)
292 TRACE("events %#x\n", event
->event
);
293 if ((event
->event
& VIRTIO_SCSI_T_EVENTS_MISSED
) != 0) {
294 ERROR("events missed\n");
295 } else switch (event
->event
) {
296 case VIRTIO_SCSI_T_TRANSPORT_RESET
:
297 ERROR("transport reset\n");
299 case VIRTIO_SCSI_T_ASYNC_NOTIFY
:
300 ERROR("async notify\n");
302 case VIRTIO_SCSI_T_PARAM_CHANGE
:
304 uint16 sense
= (event
->reason
>> 8)
305 | ((event
->reason
& 0xff) << 8);
306 if (sense
== SCSIS_ASC_CAPACITY_DATA_HAS_CHANGED
) {
307 ERROR("capacity data has changed for %x:%x\n", event
->lun
[1],
308 event
->lun
[2] << 8 | event
->lun
[3]);
309 gSCSI
->schedule_dpc(fBus
, fEventDPC
, _RescanChildBus
, this);
311 ERROR("param change, unknown reason\n");
315 ERROR("unknown event %#x\n", event
->event
);
322 VirtioSCSIController::_SubmitEvent(uint32 eventNumber
)
325 struct virtio_scsi_event
* event
= &fEventBuffers
[eventNumber
];
326 bzero(event
, sizeof(struct virtio_scsi_event
));
328 physical_entry entry
;
329 get_memory_map(event
, sizeof(struct virtio_scsi_event
), &entry
, 1);
331 fVirtio
->queue_request_v(fEventVirtioQueue
, &entry
,
332 0, 1, VirtioSCSIController::_EventCallback
, event
);
337 VirtioSCSIController::_RescanChildBus(void *cookie
)
340 VirtioSCSIController
* controller
= (VirtioSCSIController
*)cookie
;
341 device_node
*childNode
= NULL
;
342 const device_attr attrs
[] = { { NULL
} };
343 if (gDeviceManager
->get_next_child_node(controller
->fNode
, attrs
,
344 &childNode
) != B_OK
) {
345 ERROR("couldn't find the child node for %p\n", controller
->fNode
);
349 gDeviceManager
->rescan_node(childNode
);
350 TRACE("rescan done %p\n", childNode
);
351 gDeviceManager
->put_node(childNode
);