btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / add-ons / kernel / busses / scsi / virtio / VirtioSCSIController.cpp
blob9cc5edceb28bc78ea5cdc9c3fa9ab7c49fc4e8c9
1 /*
2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "VirtioSCSIPrivate.h"
9 #include <new>
10 #include <stdlib.h>
11 #include <strings.h>
13 #include <util/AutoLock.h>
16 const char *
17 get_feature_name(uint32 feature)
19 switch (feature) {
20 case VIRTIO_SCSI_F_INOUT:
21 return "in out";
22 case VIRTIO_SCSI_F_HOTPLUG:
23 return "hotplug";
24 case VIRTIO_SCSI_F_CHANGE:
25 return "change";
27 return NULL;
31 VirtioSCSIController::VirtioSCSIController(device_node *node)
33 fNode(node),
34 fVirtio(NULL),
35 fVirtioDevice(NULL),
36 fStatus(B_NO_INIT),
37 fRequest(NULL),
38 fEventDPC(NULL)
40 CALLED();
42 B_INITIALIZE_SPINLOCK(&fInterruptLock);
43 fInterruptCondition.Init(this, "virtio scsi transfer");
45 if (gSCSI->alloc_dpc(&fEventDPC) != B_OK)
46 return;
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));
63 if (fStatus != B_OK)
64 return;
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;
79 return;
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));
86 return;
89 fControlVirtioQueue = virtioQueues[0];
90 fEventVirtioQueue = virtioQueues[1];
91 fRequestVirtioQueue = virtioQueues[2];
93 for (uint32 i = 0; i < VIRTIO_SCSI_NUM_EVENTS; i++)
94 _SubmitEvent(i);
96 fStatus = fVirtio->setup_interrupt(fVirtioDevice, NULL, this);
97 if (fStatus != B_OK) {
98 ERROR("interrupt setup failed (%s)\n", strerror(fStatus));
99 return;
106 VirtioSCSIController::~VirtioSCSIController()
108 CALLED();
109 delete fRequest;
111 gSCSI->free_dpc(fEventDPC);
115 status_t
116 VirtioSCSIController::InitCheck()
118 return fStatus;
122 void
123 VirtioSCSIController::SetBus(scsi_bus bus)
125 fBus = bus;
129 void
130 VirtioSCSIController::PathInquiry(scsi_path_inquiry *info)
132 info->hba_inquiry = SCSI_PI_TAG_ABLE;
133 info->hba_misc = 0;
134 info->sim_priv = 0;
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);
149 void
150 VirtioSCSIController::GetRestrictions(uint8 targetID, bool *isATAPI,
151 bool *noAutoSense, uint32 *maxBlocks)
153 *isATAPI = false;
154 *noAutoSense = true;
155 *maxBlocks = fConfig.cmd_per_lun;
159 uchar
160 VirtioSCSIController::ResetDevice(uchar targetID, uchar targetLUN)
162 return SCSI_REQ_CMP;
166 status_t
167 VirtioSCSIController::ExecuteRequest(scsi_ccb *ccb)
169 status_t result = fRequest->Start(ccb);
170 if (result != B_OK)
171 return result;
173 if (ccb->cdb[0] == SCSI_OP_REQUEST_SENSE && fRequest->HasSense()) {
174 TRACE("request sense\n");
175 fRequest->RequestSense();
176 fRequest->Finish(false);
177 return B_OK;
180 if (ccb->target_id > fConfig.max_target) {
181 ERROR("invalid target device\n");
182 fRequest->SetStatus(SCSI_TID_INVALID);
183 fRequest->Finish(false);
184 return B_BAD_INDEX;
187 if (ccb->target_lun > fConfig.max_lun) {
188 ERROR("invalid lun device\n");
189 fRequest->SetStatus(SCSI_LUN_INVALID);
190 fRequest->Finish(false);
191 return B_BAD_INDEX;
194 if (ccb->cdb_length > VIRTIO_SCSI_CDB_SIZE) {
195 fRequest->SetStatus(SCSI_REQ_INVALID);
196 fRequest->Finish(false);
197 return B_BAD_VALUE;
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;
230 if (result != B_OK)
231 return result;
233 return fRequest->Finish(false);
237 uchar
238 VirtioSCSIController::AbortRequest(scsi_ccb *request)
240 return SCSI_REQ_CMP;
244 uchar
245 VirtioSCSIController::TerminateRequest(scsi_ccb *request)
247 return SCSI_REQ_CMP;
251 status_t
252 VirtioSCSIController::Control(uint8 targetID, uint32 op, void *buffer,
253 size_t length)
255 CALLED();
256 return B_DEV_INVALID_IOCTL;
260 void
261 VirtioSCSIController::_RequestCallback(void* driverCookie, void* cookie)
263 CALLED();
264 VirtioSCSIController* controller = (VirtioSCSIController*)driverCookie;
265 controller->_RequestInterrupt();
269 void
270 VirtioSCSIController::_RequestInterrupt()
272 SpinLocker locker(fInterruptLock);
273 fInterruptCondition.NotifyAll();
278 void
279 VirtioSCSIController::_EventCallback(void* driverCookie, void* cookie)
281 CALLED();
282 VirtioSCSIController* controller = (VirtioSCSIController*)driverCookie;
283 struct virtio_scsi_event* event = (struct virtio_scsi_event*)cookie;
284 controller->_EventInterrupt(event);
288 void
289 VirtioSCSIController::_EventInterrupt(struct virtio_scsi_event* event)
291 CALLED();
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");
298 break;
299 case VIRTIO_SCSI_T_ASYNC_NOTIFY:
300 ERROR("async notify\n");
301 break;
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);
310 } else
311 ERROR("param change, unknown reason\n");
312 break;
314 default:
315 ERROR("unknown event %#x\n", event->event);
316 break;
321 void
322 VirtioSCSIController::_SubmitEvent(uint32 eventNumber)
324 CALLED();
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);
336 void
337 VirtioSCSIController::_RescanChildBus(void *cookie)
339 CALLED();
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);
346 return;
349 gDeviceManager->rescan_node(childNode);
350 TRACE("rescan done %p\n", childNode);
351 gDeviceManager->put_node(childNode);