1/*
2 * Copyright 2004-2007, Haiku, Inc. All RightsReserved.
3 * Copyright 2002/03, Thomas Kurschel. All rights reserved.
4 *
5 * Distributed under the terms of the MIT License.
6 */
7
8/*!
9	Device scanner.
10
11	Scans SCSI busses for devices. Scanning is initiated by
12	a SCSI device node probe (see device_mgr.c)
13*/
14
15
16#include "scsi_internal.h"
17
18#include <string.h>
19#include <stdlib.h>
20
21#include <algorithm>
22
23
24/*! send TUR
25	result: true, if device answered
26		false, if there is no device
27*/
28static bool
29scsi_scan_send_tur(scsi_ccb *worker_req)
30{
31	scsi_cmd_tur *cmd = (scsi_cmd_tur *)worker_req->cdb;
32
33	SHOW_FLOW0( 3, "" );
34
35	memset( cmd, 0, sizeof( *cmd ));
36	cmd->opcode = SCSI_OP_TEST_UNIT_READY;
37
38	worker_req->sg_list = NULL;
39	worker_req->data = NULL;
40	worker_req->data_length = 0;
41	worker_req->cdb_length = sizeof(*cmd);
42	worker_req->timeout = 0;
43	worker_req->sort = -1;
44	worker_req->flags = SCSI_DIR_NONE;
45
46	scsi_sync_io( worker_req );
47
48	SHOW_FLOW( 3, "status=%x", worker_req->subsys_status );
49
50	// as this command was only for syncing, we ignore almost all errors
51	switch (worker_req->subsys_status) {
52		case SCSI_SEL_TIMEOUT:
53			// there seems to be no device around
54			return false;
55
56		default:
57			return true;
58	}
59}
60
61
62/*!	get inquiry data
63	returns true on success
64*/
65static bool
66scsi_scan_get_inquiry(scsi_ccb *worker_req, scsi_res_inquiry *new_inquiry_data)
67{
68	scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)worker_req->cdb;
69	scsi_device_info *device = worker_req->device;
70
71	SHOW_FLOW0(3, "");
72
73	// in case not whole structure gets transferred, we set remaining data to zero
74	memset(new_inquiry_data, 0, sizeof(*new_inquiry_data));
75
76	cmd->opcode = SCSI_OP_INQUIRY;
77	cmd->lun = device->target_lun;
78	cmd->evpd = 0;
79	cmd->page_code = 0;
80	cmd->allocation_length = sizeof(*new_inquiry_data);
81
82	worker_req->sg_list = NULL;
83	worker_req->data = (uchar *)new_inquiry_data;
84	worker_req->data_length = sizeof(*new_inquiry_data);
85	worker_req->cdb_length = 6;
86	worker_req->timeout = SCSI_STD_TIMEOUT;
87	worker_req->sort = -1;
88	worker_req->flags = SCSI_DIR_IN;
89
90	scsi_sync_io(worker_req);
91
92	switch (worker_req->subsys_status) {
93		case SCSI_REQ_CMP: {
94			char vendor[9], product[17], rev[5];
95
96			SHOW_FLOW0(3, "send successfully");
97
98			// we could check transmission length here, but as we reset
99			// missing bytes before, we get kind of valid data anyway (hopefully)
100
101			strlcpy(vendor, new_inquiry_data->vendor_ident, sizeof(vendor));
102			strlcpy(product, new_inquiry_data->product_ident, sizeof(product));
103			strlcpy(rev, new_inquiry_data->product_rev, sizeof(rev));
104
105			SHOW_INFO(3, "device type: %d, qualifier: %d, removable: %d, ANSI version: %d, response data format: %d\n"
106				"vendor: %s, product: %s, rev: %s",
107				new_inquiry_data->device_type, new_inquiry_data->device_qualifier,
108				new_inquiry_data->removable_medium, new_inquiry_data->ansi_version,
109				new_inquiry_data->response_data_format,
110				vendor, product, rev);
111
112			SHOW_INFO(3, "additional_length: %d", new_inquiry_data->additional_length + 4);
113
114			// time to show standards the device conforms to;
115			// unfortunately, ATAPI CD-ROM drives tend to tell that they have
116			// only minimal info (36 bytes), but still they return (valid!) 96 bytes -
117			// bad luck
118			if (std::min((int)cmd->allocation_length,
119						new_inquiry_data->additional_length + 4)
120					>= (int)offsetof(scsi_res_inquiry, _res74)) {
121				int i, previousStandard = -1;
122
123				for (i = 0; i < 8; ++i) {
124					int standard = B_BENDIAN_TO_HOST_INT16(
125						new_inquiry_data->version_descriptor[i]);
126
127					// omit standards reported twice
128					if (standard != previousStandard && standard != 0)
129						SHOW_INFO(3, "standard: %04x", standard);
130
131					previousStandard = standard;
132				}
133			}
134
135			//snooze( 1000000 );
136
137	/*		{
138				unsigned int i;
139
140				for( i = 0; i < worker_req->data_length - worker_req->data_resid; ++i ) {
141					dprintf( "%2x ", *((char *)new_inquiry_data + i) );
142				}
143
144				dprintf( "\n" );
145			}*/
146
147			return true;
148		}
149
150		default:
151			return false;
152	}
153}
154
155
156status_t
157scsi_scan_lun(scsi_bus_info *bus, uchar target_id, uchar target_lun)
158{
159	scsi_ccb *worker_req;
160	scsi_res_inquiry new_inquiry_data;
161	status_t res;
162	scsi_device_info *device;
163	bool found;
164
165	//snooze(1000000);
166
167	SHOW_FLOW(3, "%d:%d:%d", bus->path_id, target_id, target_lun);
168
169	res = scsi_force_get_device(bus, target_id, target_lun, &device);
170	if (res != B_OK)
171		goto err;
172
173	//SHOW_FLOW(3, "temp_device: %d", (int)temp_device);
174
175	worker_req = scsi_alloc_ccb(device);
176	if (worker_req == NULL) {
177		// there is no out-of-mem code
178		res = B_NO_MEMORY;
179		goto err2;
180	}
181
182	SHOW_FLOW0(3, "2");
183
184	worker_req->flags = SCSI_DIR_IN;
185
186	// to give controller a chance to transfer speed negotiation, we
187	// send a TUR first; unfortunatily, some devices don't like TURing
188	// invalid luns apart from lun 0...
189	if (device->target_lun == 0) {
190		if (!scsi_scan_send_tur(worker_req)) {
191			// TBD: need better error code like "device not found"
192			res = B_NAME_NOT_FOUND;
193			goto err3;
194		}
195	}
196
197	// get inquiry data to be used as identification
198	// and to check whether there is a device at all
199	found = scsi_scan_get_inquiry(worker_req, &new_inquiry_data)
200		&& new_inquiry_data.device_qualifier == scsi_periph_qual_connected;
201
202	// get rid of temporary device - as soon as the device is
203	// registered, it can be loaded, and we don't want two data
204	// structures for one device (the temporary and the official one)
205	scsi_free_ccb(worker_req);
206	scsi_put_forced_device(device);
207
208	if (!found) {
209		// TBD: better error code, s.a.
210		return B_NAME_NOT_FOUND;
211	}
212
213	// !danger!
214	// if a new device is detected on the same connection, all connections
215	// to the old device are disabled;
216	// scenario: you plug in a device, scan the bus, replace the device and then
217	// open it; in this case, the connection seems to be to the old device, but really
218	// is to the new one; if you scan the bus now, the opened connection is disabled
219	// - bad luck -
220	// solution 1: scan device during each scsi_init_device
221	// disadvantage: it takes time and we had to submit commands during the load
222	//   sequence, which could lead to deadlocks
223	// solution 2: device drivers must scan devices before first use
224	// disadvantage: it takes time and driver must perform a task that
225	//   the bus_manager should really take care of
226	res = scsi_register_device(bus, target_id, target_lun, &new_inquiry_data);
227	if (res == B_NAME_IN_USE) {
228		SHOW_FLOW0(3, "name in use");
229		if (scsi_force_get_device(bus, target_id, target_lun, &device) != B_OK)
230			return B_OK;
231		// the device was already registered, let's tell our child to rescan it
232		device_node *childNode = NULL;
233		const device_attr attrs[] = { { NULL } };
234		if (pnp->get_next_child_node(bus->node, attrs, &childNode) == B_OK) {
235			pnp->rescan_node(childNode);
236			pnp->put_node(childNode);
237		}
238		scsi_put_forced_device(device);
239	}
240	return B_OK;
241
242err3:
243	scsi_free_ccb(worker_req);
244err2:
245	scsi_put_forced_device(device);
246err:
247	return res;
248}
249
250
251status_t
252scsi_scan_bus(scsi_bus_info *bus)
253{
254	scsi_path_inquiry inquiry;
255
256	SHOW_FLOW0( 3, "" );
257
258	// get ID of initiator (i.e. controller)
259	uchar res = scsi_inquiry_path(bus, &inquiry);
260	if (res != SCSI_REQ_CMP)
261		return B_ERROR;
262
263	uint initiator_id = inquiry.initiator_id;
264
265	SHOW_FLOW(3, "initiator_id=%d", initiator_id);
266
267	// tell SIM to rescan bus (needed at least by IDE translator)
268	// as this function is optional for SIM, we ignore its result
269	bus->interface->scan_bus(bus->sim_cookie);
270
271	for (uint target_id = 0; target_id < bus->max_target_count; ++target_id) {
272		SHOW_FLOW(3, "target: %d", target_id);
273
274		if (target_id == initiator_id)
275			continue;
276
277		// TODO: there are a lot of devices out there that go mad if you probe
278		// anything but LUN 0, so we should probably add a black-list
279		// or something
280		for (uint lun = 0; lun < bus->max_lun_count; ++lun) {
281			SHOW_FLOW(3, "lun: %d", lun);
282
283			status_t status = scsi_scan_lun(bus, target_id, lun);
284
285			// if there is no device at lun 0, there's probably no device at all
286			if (lun == 0 && status != B_OK)
287				break;
288		}
289	}
290
291	SHOW_FLOW0(3, "done");
292	return B_OK;
293}
294