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
set_scsi_bus(scsi_sim_cookie cookie,scsi_bus bus)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
scsi_io(scsi_sim_cookie cookie,scsi_ccb * request)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
abort_io(scsi_sim_cookie cookie,scsi_ccb * request)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
reset_device(scsi_sim_cookie cookie,uchar targetID,uchar targetLUN)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
terminate_io(scsi_sim_cookie cookie,scsi_ccb * request)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
path_inquiry(scsi_sim_cookie cookie,scsi_path_inquiry * info)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
scan_bus(scsi_sim_cookie cookie)91 scan_bus(scsi_sim_cookie cookie)
92 {
93 CALLED();
94
95 return SCSI_REQ_CMP;
96 }
97
98
99 static uchar
reset_bus(scsi_sim_cookie cookie)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
get_restrictions(scsi_sim_cookie cookie,uchar targetID,bool * isATAPI,bool * noAutoSense,uint32 * maxBlocks)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
ioctl(scsi_sim_cookie cookie,uint8 targetID,uint32 op,void * buffer,size_t length)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
sim_init_bus(device_node * node,void ** _cookie)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
sim_uninit_bus(void * cookie)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
virtio_scsi_supports_device(device_node * parent)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
virtio_scsi_register_device(device_node * parent)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_luns = config.max_lun + 1;
209 uint32 max_blocks = 0x10000;
210 if (config.max_sectors != 0)
211 max_blocks = config.max_sectors;
212
213 device_attr attrs[] = {
214 { SCSI_DEVICE_MAX_TARGET_COUNT, B_UINT32_TYPE,
215 { .ui32 = max_targets }},
216 { SCSI_DEVICE_MAX_LUN_COUNT, B_UINT32_TYPE,
217 { .ui32 = max_luns }},
218 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
219 { .string = VIRTIO_SCSI_BRIDGE_PRETTY_NAME }},
220
221 // DMA properties
222 { B_DMA_MAX_SEGMENT_BLOCKS, B_UINT32_TYPE, { .ui32 = max_blocks }},
223 { B_DMA_MAX_SEGMENT_COUNT, B_UINT32_TYPE,
224 { .ui32 = config.seg_max }},
225 { NULL }
226 };
227
228 return gDeviceManager->register_node(parent, VIRTIO_SCSI_DEVICE_MODULE_NAME,
229 attrs, NULL, NULL);
230 }
231
232
233 static status_t
virtio_scsi_init_driver(device_node * node,void ** _cookie)234 virtio_scsi_init_driver(device_node *node, void **_cookie)
235 {
236 CALLED();
237 *_cookie = node;
238 return B_OK;
239 }
240
241
242 static status_t
virtio_scsi_register_child_devices(void * cookie)243 virtio_scsi_register_child_devices(void *cookie)
244 {
245 CALLED();
246 device_node *node = (device_node *)cookie;
247
248 int32 id = gDeviceManager->create_id(VIRTIO_SCSI_ID_GENERATOR);
249 if (id < 0)
250 return id;
251
252 device_attr attrs[] = {
253 { B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
254 { .string = SCSI_FOR_SIM_MODULE_NAME }},
255 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
256 { .string = VIRTIO_SCSI_CONTROLLER_PRETTY_NAME }},
257 { SCSI_DESCRIPTION_CONTROLLER_NAME, B_STRING_TYPE,
258 { .string = VIRTIO_SCSI_DEVICE_MODULE_NAME }},
259 { B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, { .ui32 = 255 }},
260 { VIRTIO_SCSI_ID_ITEM, B_UINT32_TYPE, { .ui32 = (uint32)id }},
261 { NULL }
262 };
263
264 status_t status = gDeviceManager->register_node(node,
265 VIRTIO_SCSI_SIM_MODULE_NAME, attrs, NULL, NULL);
266 if (status < B_OK)
267 gDeviceManager->free_id(VIRTIO_SCSI_ID_GENERATOR, id);
268
269 return status;
270 }
271
272
273 static status_t
std_ops(int32 op,...)274 std_ops(int32 op, ...)
275 {
276 switch (op) {
277 case B_MODULE_INIT:
278 case B_MODULE_UNINIT:
279 return B_OK;
280
281 default:
282 return B_ERROR;
283 }
284 }
285
286
287 static scsi_sim_interface sVirtioSCSISimInterface = {
288 {
289 {
290 VIRTIO_SCSI_SIM_MODULE_NAME,
291 0,
292 std_ops
293 },
294 NULL, // supported devices
295 NULL, // register node
296 sim_init_bus,
297 sim_uninit_bus,
298 NULL, // register child devices
299 NULL, // rescan
300 NULL // bus_removed
301 },
302 set_scsi_bus,
303 scsi_io,
304 abort_io,
305 reset_device,
306 terminate_io,
307 path_inquiry,
308 scan_bus,
309 reset_bus,
310 get_restrictions,
311 ioctl
312 };
313
314
315 static driver_module_info sVirtioSCSIDevice = {
316 {
317 VIRTIO_SCSI_DEVICE_MODULE_NAME,
318 0,
319 std_ops
320 },
321 virtio_scsi_supports_device,
322 virtio_scsi_register_device,
323 virtio_scsi_init_driver,
324 NULL, // uninit_driver,
325 virtio_scsi_register_child_devices,
326 NULL, // rescan
327 NULL, // device_removed
328 };
329
330
331 module_dependency module_dependencies[] = {
332 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager },
333 { SCSI_FOR_SIM_MODULE_NAME, (module_info **)&gSCSI },
334 {}
335 };
336
337
338 module_info *modules[] = {
339 (module_info *)&sVirtioSCSIDevice,
340 (module_info *)&sVirtioSCSISimInterface,
341 NULL
342 };
343