📄 sbp2.c
字号:
if (scsi_id->sbp2_device_type_and_lun != SBP2_DEVICE_TYPE_LUN_UNINITIALIZED) { scsi_id->login_orb->lun_misc |= ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun); SBP2_DEBUG("sbp2_query_logins: set lun to %d", ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun)); } SBP2_DEBUG("sbp2_login_device: lun_misc initialized"); scsi_id->login_orb->passwd_resp_lengths = ORB_SET_LOGIN_RESP_LENGTH(sizeof(struct sbp2_login_response)); SBP2_DEBUG("sbp2_login_device: passwd_resp_lengths initialized"); scsi_id->login_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->id); scsi_id->login_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) | SBP2_STATUS_FIFO_ADDRESS_HI); SBP2_DEBUG("sbp2_login_device: status FIFO initialized"); /* * Byte swap ORB if necessary */ sbp2util_cpu_to_be32_buffer(scsi_id->login_orb, sizeof(struct sbp2_login_orb)); SBP2_DEBUG("sbp2_login_device: orb byte-swapped"); sbp2util_packet_dump(scsi_id->login_orb, sizeof(struct sbp2_login_orb), "sbp2 login orb", scsi_id->login_orb_dma); /* * Initialize login response and status fifo */ memset(scsi_id->login_response, 0, sizeof(struct sbp2_login_response)); memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); SBP2_DEBUG("sbp2_login_device: login_response/status FIFO memset"); /* * Ok, let's write to the target's management agent register */ data[0] = ORB_SET_NODE_ID(hi->host->node_id); data[1] = scsi_id->login_orb_dma; sbp2util_cpu_to_be32_buffer(data, 8); atomic_set(&scsi_id->sbp2_login_complete, 0); SBP2_DEBUG("sbp2_login_device: prepared to write to %08x", (unsigned int)scsi_id->sbp2_management_agent_addr); hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); SBP2_DEBUG("sbp2_login_device: written"); /* * Wait for login status (up to 20 seconds)... */ if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, 20*HZ)) { SBP2_ERR("Error logging into SBP-2 device - login timed-out"); return(-EIO); } /* * Sanity. Make sure status returned matches login orb. */ if (scsi_id->status_block.ORB_offset_lo != scsi_id->login_orb_dma) { SBP2_ERR("Error logging into SBP-2 device - login timed-out"); return(-EIO); } /* * Check status */ if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) || STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) || STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) { SBP2_ERR("Error logging into SBP-2 device - login failed"); return(-EIO); } /* * Byte swap the login response, for use when reconnecting or * logging out. */ sbp2util_cpu_to_be32_buffer(scsi_id->login_response, sizeof(struct sbp2_login_response)); /* * Grab our command block agent address from the login response. */ SBP2_DEBUG("command_block_agent_hi = %x", (unsigned int)scsi_id->login_response->command_block_agent_hi); SBP2_DEBUG("command_block_agent_lo = %x", (unsigned int)scsi_id->login_response->command_block_agent_lo); scsi_id->sbp2_command_block_agent_addr = ((u64)scsi_id->login_response->command_block_agent_hi) << 32; scsi_id->sbp2_command_block_agent_addr |= ((u64)scsi_id->login_response->command_block_agent_lo); scsi_id->sbp2_command_block_agent_addr &= 0x0000ffffffffffffULL; SBP2_INFO("Logged into SBP-2 device"); return(0);}/* * This function is called in order to logout from a particular SBP-2 * device, usually called during driver unload. */static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id) { struct sbp2scsi_host_info *hi = scsi_id->hi; quadlet_t data[2]; SBP2_DEBUG("sbp2_logout_device"); /* * Set-up logout ORB */ scsi_id->logout_orb->reserved1 = 0x0; scsi_id->logout_orb->reserved2 = 0x0; scsi_id->logout_orb->reserved3 = 0x0; scsi_id->logout_orb->reserved4 = 0x0; scsi_id->logout_orb->login_ID_misc = ORB_SET_FUNCTION(LOGOUT_REQUEST); scsi_id->logout_orb->login_ID_misc |= ORB_SET_LOGIN_ID(scsi_id->login_response->length_login_ID); /* Notify us when complete */ scsi_id->logout_orb->login_ID_misc |= ORB_SET_NOTIFY(1); scsi_id->logout_orb->reserved5 = 0x0; scsi_id->logout_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->id); scsi_id->logout_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) | SBP2_STATUS_FIFO_ADDRESS_HI); /* * Byte swap ORB if necessary */ sbp2util_cpu_to_be32_buffer(scsi_id->logout_orb, sizeof(struct sbp2_logout_orb)); sbp2util_packet_dump(scsi_id->logout_orb, sizeof(struct sbp2_logout_orb), "sbp2 logout orb", scsi_id->logout_orb_dma); /* * Ok, let's write to the target's management agent register */ data[0] = ORB_SET_NODE_ID(hi->host->node_id); data[1] = scsi_id->logout_orb_dma; sbp2util_cpu_to_be32_buffer(data, 8); atomic_set(&scsi_id->sbp2_login_complete, 0); hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); /* Wait for device to logout...1 second. */ sbp2util_down_timeout(&scsi_id->sbp2_login_complete, HZ); SBP2_INFO("Logged out of SBP-2 device");#ifdef SBP2_USE_SCSI_ADDREM_HACK /* Now that we are logged out of the SBP-2 device, lets * try to un-hook ourselves from the SCSI subsystem */ if (scsi_remove_single_device(hi->scsi_host, 0, scsi_id->id, 0)) SBP2_INFO("Unable to disconnect SBP-2 device from the SCSI subsystem");#endif return 0;}/* * This function is called in order to reconnect to a particular SBP-2 * device, after a bus reset. */static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id) { struct sbp2scsi_host_info *hi = scsi_id->hi; quadlet_t data[2]; SBP2_DEBUG("sbp2_reconnect_device"); /* * Set-up reconnect ORB */ scsi_id->reconnect_orb->reserved1 = 0x0; scsi_id->reconnect_orb->reserved2 = 0x0; scsi_id->reconnect_orb->reserved3 = 0x0; scsi_id->reconnect_orb->reserved4 = 0x0; scsi_id->reconnect_orb->login_ID_misc = ORB_SET_FUNCTION(RECONNECT_REQUEST); scsi_id->reconnect_orb->login_ID_misc |= ORB_SET_LOGIN_ID(scsi_id->login_response->length_login_ID); /* Notify us when complete */ scsi_id->reconnect_orb->login_ID_misc |= ORB_SET_NOTIFY(1); scsi_id->reconnect_orb->reserved5 = 0x0; scsi_id->reconnect_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO + SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->id); scsi_id->reconnect_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) | SBP2_STATUS_FIFO_ADDRESS_HI); /* * Byte swap ORB if necessary */ sbp2util_cpu_to_be32_buffer(scsi_id->reconnect_orb, sizeof(struct sbp2_reconnect_orb)); sbp2util_packet_dump(scsi_id->reconnect_orb, sizeof(struct sbp2_reconnect_orb), "sbp2 reconnect orb", scsi_id->reconnect_orb_dma); /* * Initialize status fifo */ memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); /* * Ok, let's write to the target's management agent register */ data[0] = ORB_SET_NODE_ID(hi->host->node_id); data[1] = scsi_id->reconnect_orb_dma; sbp2util_cpu_to_be32_buffer(data, 8); atomic_set(&scsi_id->sbp2_login_complete, 0); hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); /* * Wait for reconnect status (up to 1 second)... */ if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, HZ)) { SBP2_ERR("Error reconnecting to SBP-2 device - reconnect timed-out"); return(-EIO); } /* * Sanity. Make sure status returned matches reconnect orb. */ if (scsi_id->status_block.ORB_offset_lo != scsi_id->reconnect_orb_dma) { SBP2_ERR("Error reconnecting to SBP-2 device - reconnect timed-out"); return(-EIO); } /* * Check status */ if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) || STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) || STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) { SBP2_ERR("Error reconnecting to SBP-2 device - reconnect failed"); return(-EIO); } SBP2_INFO("Reconnected to SBP-2 device"); return(0);}/* * This function is called in order to set the busy timeout (number of * retries to attempt) on the sbp2 device. */static int sbp2_set_busy_timeout(struct scsi_id_instance_data *scsi_id){ quadlet_t data; SBP2_DEBUG("sbp2_set_busy_timeout"); /* * Ok, let's write to the target's busy timeout register */ data = cpu_to_be32(SBP2_BUSY_TIMEOUT_VALUE); if (hpsb_node_write(scsi_id->ne, SBP2_BUSY_TIMEOUT_ADDRESS, &data, 4)) { SBP2_ERR("sbp2_set_busy_timeout error"); } return(0);}/* * This function is called to parse sbp2 device's config rom unit * directory. Used to determine things like sbp2 management agent offset, * and command set used (SCSI or RBC). */static void sbp2_parse_unit_directory(struct scsi_id_group *scsi_group, struct unit_directory *ud){ struct scsi_id_instance_data *scsi_id; struct list_head *lh; u64 management_agent_addr; u32 command_set_spec_id, command_set, unit_characteristics, firmware_revision, workarounds; int i; SBP2_DEBUG("sbp2_parse_unit_directory"); management_agent_addr = 0x0; command_set_spec_id = 0x0; command_set = 0x0; unit_characteristics = 0x0; firmware_revision = 0x0; /* Handle different fields in the unit directory, based on keys */ for (i = 0; i < ud->length; i++) { switch (CONFIG_ROM_KEY(ud->quadlets[i])) { case SBP2_CSR_OFFSET_KEY: /* Save off the management agent address */ management_agent_addr = CSR_REGISTER_BASE + (CONFIG_ROM_VALUE(ud->quadlets[i]) << 2); SBP2_DEBUG("sbp2_management_agent_addr = %x", (unsigned int) management_agent_addr); break; case SBP2_COMMAND_SET_SPEC_ID_KEY: /* Command spec organization */ command_set_spec_id = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_command_set_spec_id = %x", (unsigned int) command_set_spec_id); break; case SBP2_COMMAND_SET_KEY: /* Command set used by sbp2 device */ command_set = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_command_set = %x", (unsigned int) command_set); break; case SBP2_UNIT_CHARACTERISTICS_KEY: /* * Unit characterisitcs (orb related stuff * that I'm not yet paying attention to) */ unit_characteristics = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_unit_characteristics = %x", (unsigned int) unit_characteristics); break; case SBP2_DEVICE_TYPE_AND_LUN_KEY: /* * Device type and lun (used for * detemining type of sbp2 device) */ scsi_id = kmalloc(sizeof(*scsi_id), GFP_KERNEL); if (!scsi_id) { SBP2_ERR("Out of memory adding scsi_id, not all LUN's will be added"); break; } memset(scsi_id, 0, sizeof(*scsi_id)); scsi_id->sbp2_device_type_and_lun = CONFIG_ROM_VALUE(ud->quadlets[i]); SBP2_DEBUG("sbp2_device_type_and_lun = %x", (unsigned int) scsi_id->sbp2_device_type_and_lun); list_add_tail(&scsi_id->list, &scsi_group->scsi_id_list); break; case SBP2_FIRMWARE_REVISION_KEY: /* Firmware revision */ firmware_revision = CONFIG_ROM_VALUE(ud->quadlets[i]); if (sbp2_force_inquiry_hack) SBP2_INFO("sbp2_firmware_revision = %x", (unsigned int) firmware_revision); else SBP2_DEBUG("sbp2_firmware_revision = %x", (unsigned int) firmware_revision); break; default: break; } } /* This is the start of our broken device checking. We try to hack * around oddities and known defects. */ workarounds = 0x0; /* If the vendor id is 0xa0b8 (Symbios vendor id), then we have a * bridge with 128KB max transfer size limitation. For sanity, we * only voice this when the current sbp2_max_sectors setting * exceeds the 128k limit. By default, that is not the case. * * It would be really nice if we could detect this before the scsi * host gets initialized. That way we can down-force the * sbp2_max_sectors to account for it. That is not currently * possible. */ if ((firmware_revision & 0xffff00) == SBP2_128KB_BROKEN_FIRMWARE && (sbp2_max_sectors * 512) > (128 * 1024)) { SBP2_WARN("Node " NODE_BUS_FMT ": Bridge only supports 128KB max transfer size.", NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid)); SBP2_WARN("WARNING: Current sbp2_max_sectors setting is larger than 128KB (%d sectors)!", sbp2_max_sectors); workarounds |= SBP2_BREAKAGE_128K_MAX_TRANSFER; } /* Check for a blacklisted set of devices that require us to force * a 36 byte host inquiry. This can be overriden as a module param * (to force all hosts). */ for (i = 0; i < NUM_BROKEN_INQUIRY_DEVS; i++) { if ((firmware_revision & 0xffff00) == sbp2_broken_inquiry_list[i]) { SBP2_WARN("Node " NODE_BUS_FMT ": Using 36byte inquiry workaround", NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid)); workarounds |= SBP2_BREAKAGE_INQUIRY_HACK; break; /* No need to continue. */ } } /* If this is a logical unit directory entry, process the parent * to get the common values. */ if (ud->flags & UNIT_DIRECTORY_LUN_DIRECTORY) { sbp2_parse_unit_directory(scsi_group, ud->parent); } else { /* If our list is empty, add a base scsi_id (happens in a normal * case where there is no logical_unit_number entry */ if (list_empty(&scsi_group->scsi_id_list)) { scsi_id = kmalloc(sizeof(*scsi_id), GFP_KERNEL); if (!scsi_id) { SBP2_ERR("Out of memory adding scsi_id"); return; } memset(scsi_id, 0, sizeof(*scsi_id));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -