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
23device_manager_info *gDeviceManager;
24scsi_for_sim_interface *gSCSI;
25
26
27//	#pragma mark - SIM module interface
28
29
30static void
31set_scsi_bus(scsi_sim_cookie cookie, scsi_bus bus)
32{
33	VirtioSCSIController *sim = (VirtioSCSIController *)cookie;
34	sim->SetBus(bus);
35}
36
37
38static void
39scsi_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
48static uchar
49abort_io(scsi_sim_cookie cookie, scsi_ccb *request)
50{
51	CALLED();
52	VirtioSCSIController *sim = (VirtioSCSIController *)cookie;
53	return sim->AbortRequest(request);
54}
55
56
57static uchar
58reset_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
66static uchar
67terminate_io(scsi_sim_cookie cookie, scsi_ccb *request)
68{
69	CALLED();
70	VirtioSCSIController *sim = (VirtioSCSIController *)cookie;
71	return sim->TerminateRequest(request);
72}
73
74
75static uchar
76path_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
90static uchar
91scan_bus(scsi_sim_cookie cookie)
92{
93	CALLED();
94
95	return SCSI_REQ_CMP;
96}
97
98
99static uchar
100reset_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*/
111static void
112get_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
121static status_t
122ioctl(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
134static status_t
135sim_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
154static void
155sim_uninit_bus(void *cookie)
156{
157	CALLED();
158	VirtioSCSIController *controller = (VirtioSCSIController*)cookie;
159
160	delete controller;
161}
162
163
164//	#pragma mark -
165
166
167static float
168virtio_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
191static status_t
192virtio_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
233static status_t
234virtio_scsi_init_driver(device_node *node, void **_cookie)
235{
236	CALLED();
237	*_cookie = node;
238	return B_OK;
239}
240
241
242static status_t
243virtio_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
273static status_t
274std_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
287static 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
315static 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
331module_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
338module_info *modules[] = {
339	(module_info *)&sVirtioSCSIDevice,
340	(module_info *)&sVirtioSCSISimInterface,
341	NULL
342};
343