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 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 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 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 scsi_register_device(bus, target_id, target_lun, &new_inquiry_data); 227 228 return B_OK; 229 230 err3: 231 scsi_free_ccb(worker_req); 232 err2: 233 scsi_put_forced_device(device); 234 err: 235 return res; 236 } 237 238 239 status_t 240 scsi_scan_bus(scsi_bus_info *bus) 241 { 242 int initiator_id, target_id; 243 scsi_path_inquiry inquiry; 244 uchar res; 245 246 SHOW_FLOW0( 3, "" ); 247 248 // get ID of initiator (i.e. controller) 249 res = scsi_inquiry_path(bus, &inquiry); 250 if (res != SCSI_REQ_CMP) 251 return B_ERROR; 252 253 initiator_id = inquiry.initiator_id; 254 255 SHOW_FLOW(3, "initiator_id=%d", initiator_id); 256 257 // tell SIM to rescan bus (needed at least by IDE translator) 258 // as this function is optional for SIM, we ignore its result 259 bus->interface->scan_bus(bus->sim_cookie); 260 261 for (target_id = 0; target_id < (int)bus->max_target_count; ++target_id) { 262 int lun; 263 264 SHOW_FLOW(3, "target: %d", target_id); 265 266 if (target_id == initiator_id) 267 continue; 268 269 // TODO: there are a lot of devices out there that go mad if you probe 270 // anything but LUN 0, so we should probably add a black-list 271 // or something 272 for (lun = 0; lun <= MAX_LUN_ID; ++lun) { 273 status_t res; 274 275 SHOW_FLOW(3, "lun: %d", lun); 276 277 res = scsi_scan_lun(bus, target_id, lun); 278 279 // if there is no device at lun 0, there's probably no device at all 280 if (lun == 0 && res != SCSI_REQ_CMP) 281 break; 282 } 283 } 284 285 SHOW_FLOW0(3, "done"); 286 return B_OK; 287 } 288