xref: /haiku/src/add-ons/kernel/bus_managers/scsi/device_scan.cpp (revision a0f124211a035461631ac52b9dd0b3e274f1156c)
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 */
28 static bool
scsi_scan_send_tur(scsi_ccb * worker_req)29 scsi_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 */
65 static bool
scsi_scan_get_inquiry(scsi_ccb * worker_req,scsi_res_inquiry * new_inquiry_data)66 scsi_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 
156 status_t
scsi_scan_lun(scsi_bus_info * bus,uchar target_id,uchar target_lun)157 scsi_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 
242 err3:
243 	scsi_free_ccb(worker_req);
244 err2:
245 	scsi_put_forced_device(device);
246 err:
247 	return res;
248 }
249 
250 
251 status_t
scsi_scan_bus(scsi_bus_info * bus)252 scsi_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