📄 sbp2.c
字号:
if (!scsi_id->login_orb) { SBP2_DEBUG("%s: login_orb not alloc'd!", __FUNCTION__); return -EIO; } if (!exclusive_login) { if (sbp2_query_logins(scsi_id)) { SBP2_INFO("Device does not support any more concurrent logins"); return -EIO; } } /* Set-up login ORB, assume no password */ scsi_id->login_orb->password_hi = 0; scsi_id->login_orb->password_lo = 0; scsi_id->login_orb->login_response_lo = scsi_id->login_response_dma; scsi_id->login_orb->login_response_hi = ORB_SET_NODE_ID(hi->host->node_id); scsi_id->login_orb->lun_misc = ORB_SET_FUNCTION(SBP2_LOGIN_REQUEST); scsi_id->login_orb->lun_misc |= ORB_SET_RECONNECT(0); /* One second reconnect time */ scsi_id->login_orb->lun_misc |= ORB_SET_EXCLUSIVE(exclusive_login); /* Exclusive access to device */ scsi_id->login_orb->lun_misc |= ORB_SET_NOTIFY(1); /* Notify us of login complete */ scsi_id->login_orb->lun_misc |= ORB_SET_LUN(scsi_id->sbp2_lun); scsi_id->login_orb->passwd_resp_lengths = ORB_SET_LOGIN_RESP_LENGTH(sizeof(struct sbp2_login_response)); scsi_id->login_orb->status_fifo_hi = ORB_SET_STATUS_FIFO_HI(scsi_id->status_fifo_addr, hi->host->node_id); scsi_id->login_orb->status_fifo_lo = ORB_SET_STATUS_FIFO_LO(scsi_id->status_fifo_addr); sbp2util_cpu_to_be32_buffer(scsi_id->login_orb, sizeof(struct sbp2_login_orb)); sbp2util_packet_dump(scsi_id->login_orb, sizeof(struct sbp2_login_orb), "sbp2 login orb", scsi_id->login_orb_dma); memset(scsi_id->login_response, 0, sizeof(struct sbp2_login_response)); data[0] = ORB_SET_NODE_ID(hi->host->node_id); data[1] = scsi_id->login_orb_dma; sbp2util_cpu_to_be32_buffer(data, 8); hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); /* * Wait for login status (up to 20 seconds)... */ if (sbp2util_access_timeout(scsi_id, 20*HZ)) { SBP2_ERR("Error logging into SBP-2 device - 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 - timed out"); return -EIO; } if (STATUS_TEST_RDS(scsi_id->status_block.ORB_offset_hi_misc)) { SBP2_ERR("Error logging into SBP-2 device - 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]; int error; SBP2_DEBUG_ENTER(); /* * 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(SBP2_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_hi = ORB_SET_STATUS_FIFO_HI(scsi_id->status_fifo_addr, hi->host->node_id); scsi_id->logout_orb->status_fifo_lo = ORB_SET_STATUS_FIFO_LO(scsi_id->status_fifo_addr); /* * 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); error = hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); if (error) return error; /* Wait for device to logout...1 second. */ if (sbp2util_access_timeout(scsi_id, HZ)) return -EIO; SBP2_INFO("Logged out of SBP-2 device"); 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]; int error; SBP2_DEBUG_ENTER(); /* * 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(SBP2_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_hi = ORB_SET_STATUS_FIFO_HI(scsi_id->status_fifo_addr, hi->host->node_id); scsi_id->reconnect_orb->status_fifo_lo = ORB_SET_STATUS_FIFO_LO(scsi_id->status_fifo_addr); /* * 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); data[0] = ORB_SET_NODE_ID(hi->host->node_id); data[1] = scsi_id->reconnect_orb_dma; sbp2util_cpu_to_be32_buffer(data, 8); error = hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8); if (error) return error; /* * Wait for reconnect status (up to 1 second)... */ if (sbp2util_access_timeout(scsi_id, HZ)) { SBP2_ERR("Error reconnecting to SBP-2 device - 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 - timed out"); return -EIO; } if (STATUS_TEST_RDS(scsi_id->status_block.ORB_offset_hi_misc)) { SBP2_ERR("Error reconnecting to SBP-2 device - failed"); return -EIO; } HPSB_DEBUG("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_ENTER(); data = cpu_to_be32(SBP2_BUSY_TIMEOUT_VALUE); if (hpsb_node_write(scsi_id->ne, SBP2_BUSY_TIMEOUT_ADDRESS, &data, 4)) SBP2_ERR("%s error", __FUNCTION__); 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_instance_data *scsi_id, struct unit_directory *ud){ struct csr1212_keyval *kv; struct csr1212_dentry *dentry; u64 management_agent_addr; u32 command_set_spec_id, command_set, unit_characteristics, firmware_revision; unsigned workarounds; int i; SBP2_DEBUG_ENTER(); 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 */ csr1212_for_each_dir_entry(ud->ne->csr, kv, ud->ud_kv, dentry) { switch (kv->key.id) { case CSR1212_KV_ID_DEPENDENT_INFO: if (kv->key.type == CSR1212_KV_TYPE_CSR_OFFSET) { /* Save off the management agent address */ management_agent_addr = CSR1212_REGISTER_SPACE_BASE + (kv->value.csr_offset << 2); SBP2_DEBUG("sbp2_management_agent_addr = %x", (unsigned int)management_agent_addr); } else if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) { scsi_id->sbp2_lun = ORB_SET_LUN(kv->value.immediate); } break; case SBP2_COMMAND_SET_SPEC_ID_KEY: /* Command spec organization */ command_set_spec_id = kv->value.immediate; 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 = kv->value.immediate; 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 = kv->value.immediate; SBP2_DEBUG("sbp2_unit_characteristics = %x", (unsigned int)unit_characteristics); break; case SBP2_FIRMWARE_REVISION_KEY: /* Firmware revision */ firmware_revision = kv->value.immediate; SBP2_DEBUG("sbp2_firmware_revision = %x", (unsigned int)firmware_revision); break; default: break; } } workarounds = sbp2_default_workarounds; if (!(workarounds & SBP2_WORKAROUND_OVERRIDE)) for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) { if (sbp2_workarounds_table[i].firmware_revision && sbp2_workarounds_table[i].firmware_revision != (firmware_revision & 0xffff00)) continue; if (sbp2_workarounds_table[i].model_id && sbp2_workarounds_table[i].model_id != ud->model_id) continue; workarounds |= sbp2_workarounds_table[i].workarounds; break; } if (workarounds) SBP2_INFO("Workarounds for node " NODE_BUS_FMT ": 0x%x " "(firmware_revision 0x%06x, vendor_id 0x%06x," " model_id 0x%06x)", NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid), workarounds, firmware_revision, ud->vendor_id ? ud->vendor_id : ud->ne->vendor_id, ud->model_id); /* We would need one SCSI host template for each target to adjust * max_sectors on the fly, therefore warn only. */ if (workarounds & SBP2_WORKAROUND_128K_MAX_TRANS && (max_sectors * 512) > (128 * 1024)) SBP2_WARN("Node " NODE_BUS_FMT ": Bridge only supports 128KB " "max transfer size. WARNING: Current max_sectors " "setting is larger than 128KB (%d sectors)", NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid), max_sectors); /* If this is a logical unit directory entry, process the parent * to get the values. */ if (ud->flags & UNIT_DIRECTORY_LUN_DIRECTORY) { struct unit_directory *parent_ud = container_of(ud->device.parent, struct unit_directory, device); sbp2_parse_unit_directory(scsi_id, parent_ud); } else { scsi_id->sbp2_management_agent_addr = management_agent_addr; scsi_id->sbp2_command_set_spec_id = command_set_spec_id; scsi_id->sbp2_command_set = command_set; scsi_id->sbp2_unit_characteristics = unit_characteristics; scsi_id->sbp2_firmware_revision = firmware_revision; scsi_id->workarounds = workarounds; if (ud->flags & UNIT_DIRECTORY_HAS_LUN) scsi_id->sbp2_lun = ORB_SET_LUN(ud->lun); }}#define SBP2_PAYLOAD_TO_BYTES(p) (1 << ((p) + 2))/* * This function is called in order to determine the max speed and packet * size we can use in our ORBs. Note, that we (the driver and host) only * initiate the transaction. The SBP-2 device actually transfers the data * (by reading from the DMA area we tell it). This means that the SBP-2 * device decides the actual maximum data it can transfer. We just tell it * the speed that it needs to use, and the max_rec the host supports, and * it takes care of the rest. */static int sbp2_max_speed_and_size(struct scsi_id_instance_data *scsi_id){ struct sbp2scsi_host_info *hi = scsi_id->hi; u8 payload; SBP2_DEBUG_ENTER(); scsi_id->speed_code = hi->host->speed[NODEID_TO_NODE(scsi_id->ne->nodeid)]; /* Bump down our speed if the user requested it */ if (scsi_id->speed_code > max_speed) { scsi_id->speed_code = max_speed; SBP2_ERR("Forcing SBP-2 max speed down to %s", hpsb_speedto_str[scsi_id->speed_code]); } /* Payload size is the lesser of what our speed supports and what * our host supports. */ payload = min(sbp2_speedto_max_payload[scsi_id->speed_code], (u8) (hi->host->csr.max_rec - 1)); /* If physical DMA is off, work around limitation in ohci1394: * packet size must not exceed PAGE_SIZE */ if (scsi_id->ne->host->low_addr_space < (1ULL << 32)) while (SBP2_PAYLOAD_TO_BYTES(payload) + 24 > PAGE_SIZE && payload) payload--; HPSB_DEBUG("Node " NODE_BUS_FMT ": Max speed [%s] - Max payload [%u]", NODE_BUS_ARGS(hi->host, scsi_id->ne->nodeid), hpsb_speedto_str[scsi_id->speed_code], SBP2_PAYLOAD_TO_BYTES(payload)); scsi_id->max_payload_size = payload; return 0;}/* * This function is called in order to perform a SBP-2 agent reset. */static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait){ quadlet_t data;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -