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