BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / bus_managers / scsi / device_scan.cpp
blobbb0ffe83b5080865d51be5d1a7a3454517f6ca68
1 /*
2 * Copyright 2004-2007, Haiku, Inc. All RightsReserved.
3 * Copyright 2002/03, Thomas Kurschel. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 */
8 /*!
9 Device scanner.
11 Scans SCSI busses for devices. Scanning is initiated by
12 a SCSI device node probe (see device_mgr.c)
16 #include "scsi_internal.h"
18 #include <string.h>
19 #include <stdlib.h>
21 #include <algorithm>
24 /*! send TUR
25 result: true, if device answered
26 false, if there is no device
28 static bool
29 scsi_scan_send_tur(scsi_ccb *worker_req)
31 scsi_cmd_tur *cmd = (scsi_cmd_tur *)worker_req->cdb;
33 SHOW_FLOW0( 3, "" );
35 memset( cmd, 0, sizeof( *cmd ));
36 cmd->opcode = SCSI_OP_TEST_UNIT_READY;
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;
46 scsi_sync_io( worker_req );
48 SHOW_FLOW( 3, "status=%x", worker_req->subsys_status );
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;
56 default:
57 return true;
62 /*! get inquiry data
63 returns true on success
65 static bool
66 scsi_scan_get_inquiry(scsi_ccb *worker_req, scsi_res_inquiry *new_inquiry_data)
68 scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)worker_req->cdb;
69 scsi_device_info *device = worker_req->device;
71 SHOW_FLOW0(3, "");
73 // in case not whole structure gets transferred, we set remaining data to zero
74 memset(new_inquiry_data, 0, sizeof(*new_inquiry_data));
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);
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;
90 scsi_sync_io(worker_req);
92 switch (worker_req->subsys_status) {
93 case SCSI_REQ_CMP: {
94 char vendor[9], product[17], rev[5];
96 SHOW_FLOW0(3, "send successfully");
98 // we could check transmission length here, but as we reset
99 // missing bytes before, we get kind of valid data anyway (hopefully)
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));
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);
112 SHOW_INFO(3, "additional_length: %d", new_inquiry_data->additional_length + 4);
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;
123 for (i = 0; i < 8; ++i) {
124 int standard = B_BENDIAN_TO_HOST_INT16(
125 new_inquiry_data->version_descriptor[i]);
127 // omit standards reported twice
128 if (standard != previousStandard && standard != 0)
129 SHOW_INFO(3, "standard: %04x", standard);
131 previousStandard = standard;
135 //snooze( 1000000 );
137 /* {
138 unsigned int i;
140 for( i = 0; i < worker_req->data_length - worker_req->data_resid; ++i ) {
141 dprintf( "%2x ", *((char *)new_inquiry_data + i) );
144 dprintf( "\n" );
147 return true;
150 default:
151 return false;
156 status_t
157 scsi_scan_lun(scsi_bus_info *bus, uchar target_id, uchar target_lun)
159 scsi_ccb *worker_req;
160 scsi_res_inquiry new_inquiry_data;
161 status_t res;
162 scsi_device_info *device;
163 bool found;
165 //snooze(1000000);
167 SHOW_FLOW(3, "%d:%d:%d", bus->path_id, target_id, target_lun);
169 res = scsi_force_get_device(bus, target_id, target_lun, &device);
170 if (res != B_OK)
171 goto err;
173 //SHOW_FLOW(3, "temp_device: %d", (int)temp_device);
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;
182 SHOW_FLOW0(3, "2");
184 worker_req->flags = SCSI_DIR_IN;
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;
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;
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);
208 if (!found) {
209 // TBD: better error code, s.a.
210 return B_NAME_NOT_FOUND;
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);
238 scsi_put_forced_device(device);
240 return B_OK;
242 err3:
243 scsi_free_ccb(worker_req);
244 err2:
245 scsi_put_forced_device(device);
246 err:
247 return res;
251 status_t
252 scsi_scan_bus(scsi_bus_info *bus)
254 scsi_path_inquiry inquiry;
256 SHOW_FLOW0( 3, "" );
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;
263 uint initiator_id = inquiry.initiator_id;
265 SHOW_FLOW(3, "initiator_id=%d", initiator_id);
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);
271 for (uint target_id = 0; target_id < bus->max_target_count; ++target_id) {
272 SHOW_FLOW(3, "target: %d", target_id);
274 if (target_id == initiator_id)
275 continue;
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);
283 status_t status = scsi_scan_lun(bus, target_id, lun);
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;
291 SHOW_FLOW0(3, "done");
292 return B_OK;