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