* Copyright 2004-2007, Haiku, Inc. All RightsReserved.
* Copyright 2002/03, Thomas Kurschel. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
Device scanner.
Scans SCSI busses for devices. Scanning is initiated by
a SCSI device node probe (see device_mgr.c)
*/
#include "scsi_internal.h"
#include <string.h>
#include <stdlib.h>
#include <algorithm>
result: true, if device answered
false, if there is no device
*/
static bool
scsi_scan_send_tur(scsi_ccb *worker_req)
{
scsi_cmd_tur *cmd = (scsi_cmd_tur *)worker_req->cdb;
SHOW_FLOW0( 3, "" );
memset( cmd, 0, sizeof( *cmd ));
cmd->opcode = SCSI_OP_TEST_UNIT_READY;
worker_req->sg_list = NULL;
worker_req->data = NULL;
worker_req->data_length = 0;
worker_req->cdb_length = sizeof(*cmd);
worker_req->timeout = 0;
worker_req->sort = -1;
worker_req->flags = SCSI_DIR_NONE;
scsi_sync_io( worker_req );
SHOW_FLOW( 3, "status=%x", worker_req->subsys_status );
switch (worker_req->subsys_status) {
case SCSI_SEL_TIMEOUT:
return false;
default:
return true;
}
}
returns true on success
*/
static bool
scsi_scan_get_inquiry(scsi_ccb *worker_req, scsi_res_inquiry *new_inquiry_data)
{
scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)worker_req->cdb;
scsi_device_info *device = worker_req->device;
SHOW_FLOW0(3, "");
memset(new_inquiry_data, 0, sizeof(*new_inquiry_data));
cmd->opcode = SCSI_OP_INQUIRY;
cmd->lun = device->target_lun;
cmd->evpd = 0;
cmd->page_code = 0;
cmd->allocation_length = sizeof(*new_inquiry_data);
worker_req->sg_list = NULL;
worker_req->data = (uchar *)new_inquiry_data;
worker_req->data_length = sizeof(*new_inquiry_data);
worker_req->cdb_length = 6;
worker_req->timeout = SCSI_STD_TIMEOUT;
worker_req->sort = -1;
worker_req->flags = SCSI_DIR_IN;
scsi_sync_io(worker_req);
switch (worker_req->subsys_status) {
case SCSI_REQ_CMP: {
char vendor[9], product[17], rev[5];
SHOW_FLOW0(3, "send successfully");
strlcpy(vendor, new_inquiry_data->vendor_ident, sizeof(vendor));
strlcpy(product, new_inquiry_data->product_ident, sizeof(product));
strlcpy(rev, new_inquiry_data->product_rev, sizeof(rev));
SHOW_INFO(3, "device type: %d, qualifier: %d, removable: %d, ANSI version: %d, response data format: %d\n"
"vendor: %s, product: %s, rev: %s",
new_inquiry_data->device_type, new_inquiry_data->device_qualifier,
new_inquiry_data->removable_medium, new_inquiry_data->ansi_version,
new_inquiry_data->response_data_format,
vendor, product, rev);
SHOW_INFO(3, "additional_length: %d", new_inquiry_data->additional_length + 4);
if (std::min((int)cmd->allocation_length,
new_inquiry_data->additional_length + 4)
>= (int)offsetof(scsi_res_inquiry, _res74)) {
int i, previousStandard = -1;
for (i = 0; i < 8; ++i) {
int standard = B_BENDIAN_TO_HOST_INT16(
new_inquiry_data->version_descriptor[i]);
if (standard != previousStandard && standard != 0)
SHOW_INFO(3, "standard: %04x", standard);
previousStandard = standard;
}
}
unsigned int i;
for( i = 0; i < worker_req->data_length - worker_req->data_resid; ++i ) {
dprintf( "%2x ", *((char *)new_inquiry_data + i) );
}
dprintf( "\n" );
}*/
return true;
}
default:
return false;
}
}
status_t
scsi_scan_lun(scsi_bus_info *bus, uchar target_id, uchar target_lun)
{
scsi_ccb *worker_req;
scsi_res_inquiry new_inquiry_data;
status_t res;
scsi_device_info *device;
bool found;
SHOW_FLOW(3, "%d:%d:%d", bus->path_id, target_id, target_lun);
res = scsi_force_get_device(bus, target_id, target_lun, &device);
if (res != B_OK)
goto err;
worker_req = scsi_alloc_ccb(device);
if (worker_req == NULL) {
res = B_NO_MEMORY;
goto err2;
}
SHOW_FLOW0(3, "2");
worker_req->flags = SCSI_DIR_IN;
if (device->target_lun == 0) {
if (!scsi_scan_send_tur(worker_req)) {
res = B_NAME_NOT_FOUND;
goto err3;
}
}
found = scsi_scan_get_inquiry(worker_req, &new_inquiry_data)
&& new_inquiry_data.device_qualifier == scsi_periph_qual_connected;
scsi_free_ccb(worker_req);
scsi_put_forced_device(device);
if (!found) {
return B_NAME_NOT_FOUND;
}
res = scsi_register_device(bus, target_id, target_lun, &new_inquiry_data);
if (res == B_NAME_IN_USE) {
SHOW_FLOW0(3, "name in use");
if (scsi_force_get_device(bus, target_id, target_lun, &device) != B_OK)
return B_OK;
device_node *childNode = NULL;
const device_attr attrs[] = { { NULL } };
if (pnp->get_next_child_node(bus->node, attrs, &childNode) == B_OK) {
pnp->rescan_node(childNode);
pnp->put_node(childNode);
}
scsi_put_forced_device(device);
}
return B_OK;
err3:
scsi_free_ccb(worker_req);
err2:
scsi_put_forced_device(device);
err:
return res;
}
status_t
scsi_scan_bus(scsi_bus_info *bus)
{
scsi_path_inquiry inquiry;
SHOW_FLOW0( 3, "" );
uchar res = scsi_inquiry_path(bus, &inquiry);
if (res != SCSI_REQ_CMP)
return B_ERROR;
uint initiator_id = inquiry.initiator_id;
SHOW_FLOW(3, "initiator_id=%d", initiator_id);
bus->interface->scan_bus(bus->sim_cookie);
for (uint target_id = 0; target_id < bus->max_target_count; ++target_id) {
SHOW_FLOW(3, "target: %d", target_id);
if (target_id == initiator_id)
continue;
for (uint lun = 0; lun < bus->max_lun_count; ++lun) {
SHOW_FLOW(3, "lun: %d", lun);
status_t status = scsi_scan_lun(bus, target_id, lun);
if (lun == 0 && status != B_OK)
break;
}
}
SHOW_FLOW0(3, "done");
return B_OK;
}