xref: /haiku/src/add-ons/kernel/busses/scsi/virtio/virtio_scsi.cpp (revision 99d1318ec02694fc520a0dc38ae38565db7e8c3c)
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_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
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
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
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