1 /* 2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "VirtioSCSIPrivate.h" 8 9 #include <new> 10 #include <stdlib.h> 11 #include <string.h> 12 13 14 #define VIRTIO_SCSI_ID_GENERATOR "virtio_scsi/id" 15 #define VIRTIO_SCSI_ID_ITEM "virtio_scsi/id" 16 #define VIRTIO_SCSI_BRIDGE_PRETTY_NAME "Virtio SCSI Bridge" 17 #define VIRTIO_SCSI_CONTROLLER_PRETTY_NAME "Virtio SCSI Controller" 18 19 #define VIRTIO_SCSI_DEVICE_MODULE_NAME "busses/scsi/virtio_scsi/driver_v1" 20 #define VIRTIO_SCSI_SIM_MODULE_NAME "busses/scsi/virtio_scsi/sim/driver_v1" 21 22 23 device_manager_info *gDeviceManager; 24 scsi_for_sim_interface *gSCSI; 25 26 27 // #pragma mark - SIM module interface 28 29 30 static void 31 set_scsi_bus(scsi_sim_cookie cookie, scsi_bus bus) 32 { 33 VirtioSCSIController *sim = (VirtioSCSIController *)cookie; 34 sim->SetBus(bus); 35 } 36 37 38 static void 39 scsi_io(scsi_sim_cookie cookie, scsi_ccb *request) 40 { 41 CALLED(); 42 VirtioSCSIController *sim = (VirtioSCSIController *)cookie; 43 if (sim->ExecuteRequest(request) == B_BUSY) 44 gSCSI->requeue(request, true); 45 } 46 47 48 static uchar 49 abort_io(scsi_sim_cookie cookie, scsi_ccb *request) 50 { 51 CALLED(); 52 VirtioSCSIController *sim = (VirtioSCSIController *)cookie; 53 return sim->AbortRequest(request); 54 } 55 56 57 static uchar 58 reset_device(scsi_sim_cookie cookie, uchar targetID, uchar targetLUN) 59 { 60 CALLED(); 61 VirtioSCSIController *sim = (VirtioSCSIController *)cookie; 62 return sim->ResetDevice(targetID, targetLUN); 63 } 64 65 66 static uchar 67 terminate_io(scsi_sim_cookie cookie, scsi_ccb *request) 68 { 69 CALLED(); 70 VirtioSCSIController *sim = (VirtioSCSIController *)cookie; 71 return sim->TerminateRequest(request); 72 } 73 74 75 static uchar 76 path_inquiry(scsi_sim_cookie cookie, scsi_path_inquiry *info) 77 { 78 CALLED(); 79 80 VirtioSCSIController *sim = (VirtioSCSIController *)cookie; 81 if (sim->Bus() == NULL) 82 return SCSI_NO_HBA; 83 84 sim->PathInquiry(info); 85 return SCSI_REQ_CMP; 86 } 87 88 89 //! this is called immediately before the SCSI bus manager scans the bus 90 static uchar 91 scan_bus(scsi_sim_cookie cookie) 92 { 93 CALLED(); 94 95 return SCSI_REQ_CMP; 96 } 97 98 99 static uchar 100 reset_bus(scsi_sim_cookie cookie) 101 { 102 CALLED(); 103 104 return SCSI_REQ_CMP; 105 } 106 107 108 /*! Get restrictions of one device 109 (used for non-SCSI transport protocols and bug fixes) 110 */ 111 static void 112 get_restrictions(scsi_sim_cookie cookie, uchar targetID, bool *isATAPI, 113 bool *noAutoSense, uint32 *maxBlocks) 114 { 115 CALLED(); 116 VirtioSCSIController *sim = (VirtioSCSIController *)cookie; 117 sim->GetRestrictions(targetID, isATAPI, noAutoSense, maxBlocks); 118 } 119 120 121 static status_t 122 ioctl(scsi_sim_cookie cookie, uint8 targetID, uint32 op, void *buffer, 123 size_t length) 124 { 125 CALLED(); 126 VirtioSCSIController *sim = (VirtioSCSIController *)cookie; 127 return sim->Control(targetID, op, buffer, length); 128 } 129 130 131 // #pragma mark - 132 133 134 static status_t 135 sim_init_bus(device_node *node, void **_cookie) 136 { 137 CALLED(); 138 139 VirtioSCSIController *controller = new(std::nothrow) 140 VirtioSCSIController(node); 141 if (controller == NULL) 142 return B_NO_MEMORY; 143 status_t status = controller->InitCheck(); 144 if (status < B_OK) { 145 delete controller; 146 return status; 147 } 148 149 *_cookie = controller; 150 return B_OK; 151 } 152 153 154 static void 155 sim_uninit_bus(void *cookie) 156 { 157 CALLED(); 158 VirtioSCSIController *controller = (VirtioSCSIController*)cookie; 159 160 delete controller; 161 } 162 163 164 // #pragma mark - 165 166 167 static float 168 virtio_scsi_supports_device(device_node *parent) 169 { 170 const char *bus; 171 uint16 deviceType; 172 173 // make sure parent is really the Virtio bus manager 174 if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 175 return -1; 176 177 if (strcmp(bus, "virtio")) 178 return 0.0; 179 180 // check whether it's really a Virtio SCSI Device 181 if (gDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM, 182 &deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_SCSI) 183 return 0.0; 184 185 TRACE("Virtio SCSI device found!\n"); 186 187 return 0.6f; 188 } 189 190 191 static status_t 192 virtio_scsi_register_device(device_node *parent) 193 { 194 CALLED(); 195 virtio_device_interface* virtio = NULL; 196 virtio_device* virtioDevice = NULL; 197 struct virtio_scsi_config config; 198 199 gDeviceManager->get_driver(parent, (driver_module_info **)&virtio, 200 (void **)&virtioDevice); 201 202 status_t status = virtio->read_device_config(virtioDevice, 0, &config, 203 sizeof(struct virtio_scsi_config)); 204 if (status != B_OK) 205 return status; 206 207 uint32 max_targets = config.max_target + 1; 208 uint32 max_blocks = 0x10000; 209 if (config.max_sectors != 0) 210 max_blocks = config.max_sectors; 211 212 device_attr attrs[] = { 213 { SCSI_DEVICE_MAX_TARGET_COUNT, B_UINT32_TYPE, 214 { ui32: max_targets }}, 215 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, 216 { string: VIRTIO_SCSI_BRIDGE_PRETTY_NAME }}, 217 218 // DMA properties 219 { B_DMA_MAX_SEGMENT_BLOCKS, B_UINT32_TYPE, { ui32: max_blocks }}, 220 { B_DMA_MAX_SEGMENT_COUNT, B_UINT32_TYPE, 221 { ui32: config.seg_max }}, 222 { NULL } 223 }; 224 225 return gDeviceManager->register_node(parent, VIRTIO_SCSI_DEVICE_MODULE_NAME, 226 attrs, NULL, NULL); 227 } 228 229 230 static status_t 231 virtio_scsi_init_driver(device_node *node, void **_cookie) 232 { 233 CALLED(); 234 *_cookie = node; 235 return B_OK; 236 } 237 238 239 static status_t 240 virtio_scsi_register_child_devices(void *cookie) 241 { 242 CALLED(); 243 device_node *node = (device_node *)cookie; 244 245 int32 id = gDeviceManager->create_id(VIRTIO_SCSI_ID_GENERATOR); 246 if (id < 0) 247 return id; 248 249 device_attr attrs[] = { 250 { B_DEVICE_FIXED_CHILD, B_STRING_TYPE, 251 { string: SCSI_FOR_SIM_MODULE_NAME }}, 252 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, 253 { string: VIRTIO_SCSI_CONTROLLER_PRETTY_NAME }}, 254 { SCSI_DESCRIPTION_CONTROLLER_NAME, B_STRING_TYPE, 255 { string: VIRTIO_SCSI_DEVICE_MODULE_NAME }}, 256 { B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, { ui32: 255 }}, 257 { VIRTIO_SCSI_ID_ITEM, B_UINT32_TYPE, { ui32: (uint32)id }}, 258 { NULL } 259 }; 260 261 status_t status = gDeviceManager->register_node(node, 262 VIRTIO_SCSI_SIM_MODULE_NAME, attrs, NULL, NULL); 263 if (status < B_OK) 264 gDeviceManager->free_id(VIRTIO_SCSI_ID_GENERATOR, id); 265 266 return status; 267 } 268 269 270 static status_t 271 std_ops(int32 op, ...) 272 { 273 switch (op) { 274 case B_MODULE_INIT: 275 case B_MODULE_UNINIT: 276 return B_OK; 277 278 default: 279 return B_ERROR; 280 } 281 } 282 283 284 static scsi_sim_interface sVirtioSCSISimInterface = { 285 { 286 { 287 VIRTIO_SCSI_SIM_MODULE_NAME, 288 0, 289 std_ops 290 }, 291 NULL, // supported devices 292 NULL, // register node 293 sim_init_bus, 294 sim_uninit_bus, 295 NULL, // register child devices 296 NULL, // rescan 297 NULL // bus_removed 298 }, 299 set_scsi_bus, 300 scsi_io, 301 abort_io, 302 reset_device, 303 terminate_io, 304 path_inquiry, 305 scan_bus, 306 reset_bus, 307 get_restrictions, 308 ioctl 309 }; 310 311 312 static driver_module_info sVirtioSCSIDevice = { 313 { 314 VIRTIO_SCSI_DEVICE_MODULE_NAME, 315 0, 316 std_ops 317 }, 318 virtio_scsi_supports_device, 319 virtio_scsi_register_device, 320 virtio_scsi_init_driver, 321 NULL, // uninit_driver, 322 virtio_scsi_register_child_devices, 323 NULL, // rescan 324 NULL, // device_removed 325 }; 326 327 328 module_dependency module_dependencies[] = { 329 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager }, 330 { SCSI_FOR_SIM_MODULE_NAME, (module_info **)&gSCSI }, 331 {} 332 }; 333 334 335 module_info *modules[] = { 336 (module_info *)&sVirtioSCSIDevice, 337 (module_info *)&sVirtioSCSISimInterface, 338 NULL 339 }; 340