📄 sbp2.c
字号:
*/ switch (scsi_dir) { case SCSI_DATA_NONE: orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER; break; case SCSI_DATA_WRITE: orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA; break; case SCSI_DATA_READ: orb_direction = ORB_DIRECTION_READ_FROM_MEDIA; break; case SCSI_DATA_UNKNOWN: default: SBP2_ERR("SCSI data transfer direction not specified. " "Update the SBP2 direction table in sbp2.h if " "necessary for your application"); print_command (scsi_cmd); orb_direction = sbp2scsi_direction_table[*scsi_cmd]; break; } /* * Set-up our pagetable stuff... unfortunately, this has become * messier than I'd like. Need to clean this up a bit. ;-) */ if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) { SBP2_DEBUG("No data transfer"); /* * Handle no data transfer */ command_orb->data_descriptor_hi = 0x0; command_orb->data_descriptor_lo = 0x0; command_orb->misc |= ORB_SET_DIRECTION(1); } else if (scsi_use_sg) { SBP2_DEBUG("Use scatter/gather"); /* * Special case if only one element (and less than 64KB in size) */ if ((scsi_use_sg == 1) && (sgpnt[0].length <= SBP2_MAX_SG_ELEMENT_LENGTH)) { SBP2_DEBUG("Only one s/g element"); command->dma_dir = dma_dir; command->dma_size = sgpnt[0].length; command->dma_type = CMD_DMA_PAGE; command->cmd_dma = pci_map_page(hi->host->pdev, sgpnt[0].page, sgpnt[0].offset, command->dma_size, command->dma_dir); SBP2_DMA_ALLOC("single page scatter element"); command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); command_orb->data_descriptor_lo = command->cmd_dma; command_orb->misc |= ORB_SET_DATA_SIZE(command->dma_size); command_orb->misc |= ORB_SET_DIRECTION(orb_direction); } else { int count = pci_map_sg(hi->host->pdev, sgpnt, scsi_use_sg, dma_dir); SBP2_DMA_ALLOC("scatter list"); command->dma_size = scsi_use_sg; command->dma_dir = dma_dir; command->sge_buffer = sgpnt; /* use page tables (s/g) */ command_orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1); command_orb->misc |= ORB_SET_DIRECTION(orb_direction); command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); command_orb->data_descriptor_lo = command->sge_dma; /* * Loop through and fill out our sbp-2 page tables * (and split up anything too large) */ for (i = 0, sg_count = 0 ; i < count; i++, sgpnt++) { sg_len = sg_dma_len(sgpnt); sg_addr = sg_dma_address(sgpnt); while (sg_len) { scatter_gather_element[sg_count].segment_base_lo = sg_addr; if (sg_len > SBP2_MAX_SG_ELEMENT_LENGTH) { scatter_gather_element[sg_count].length_segment_base_hi = PAGE_TABLE_SET_SEGMENT_LENGTH(SBP2_MAX_SG_ELEMENT_LENGTH); sg_addr += SBP2_MAX_SG_ELEMENT_LENGTH; sg_len -= SBP2_MAX_SG_ELEMENT_LENGTH; } else { scatter_gather_element[sg_count].length_segment_base_hi = PAGE_TABLE_SET_SEGMENT_LENGTH(sg_len); sg_len = 0; } sg_count++; } } /* Number of page table (s/g) elements */ command_orb->misc |= ORB_SET_DATA_SIZE(sg_count); sbp2util_packet_dump(scatter_gather_element, (sizeof(struct sbp2_unrestricted_page_table)) * sg_count, "sbp2 s/g list", command->sge_dma); /* * Byte swap page tables if necessary */ sbp2util_cpu_to_be32_buffer(scatter_gather_element, (sizeof(struct sbp2_unrestricted_page_table)) * sg_count); } } else { SBP2_DEBUG("No scatter/gather"); command->dma_dir = dma_dir; command->dma_size = scsi_request_bufflen; command->dma_type = CMD_DMA_SINGLE; command->cmd_dma = pci_map_single (hi->host->pdev, scsi_request_buffer, command->dma_size, command->dma_dir); SBP2_DMA_ALLOC("single bulk"); /* * Handle case where we get a command w/o s/g enabled (but * check for transfers larger than 64K) */ if (scsi_request_bufflen <= SBP2_MAX_SG_ELEMENT_LENGTH) { command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); command_orb->data_descriptor_lo = command->cmd_dma; command_orb->misc |= ORB_SET_DATA_SIZE(scsi_request_bufflen); command_orb->misc |= ORB_SET_DIRECTION(orb_direction); /* * Sanity, in case our direction table is not * up-to-date */ if (!scsi_request_bufflen) { command_orb->data_descriptor_hi = 0x0; command_orb->data_descriptor_lo = 0x0; command_orb->misc |= ORB_SET_DIRECTION(1); } } else { /* * Need to turn this into page tables, since the * buffer is too large. */ command_orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); command_orb->data_descriptor_lo = command->sge_dma; /* Use page tables (s/g) */ command_orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1); command_orb->misc |= ORB_SET_DIRECTION(orb_direction); /* * fill out our sbp-2 page tables (and split up * the large buffer) */ sg_count = 0; sg_len = scsi_request_bufflen; sg_addr = command->cmd_dma; while (sg_len) { scatter_gather_element[sg_count].segment_base_lo = sg_addr; if (sg_len > SBP2_MAX_SG_ELEMENT_LENGTH) { scatter_gather_element[sg_count].length_segment_base_hi = PAGE_TABLE_SET_SEGMENT_LENGTH(SBP2_MAX_SG_ELEMENT_LENGTH); sg_addr += SBP2_MAX_SG_ELEMENT_LENGTH; sg_len -= SBP2_MAX_SG_ELEMENT_LENGTH; } else { scatter_gather_element[sg_count].length_segment_base_hi = PAGE_TABLE_SET_SEGMENT_LENGTH(sg_len); sg_len = 0; } sg_count++; } /* Number of page table (s/g) elements */ command_orb->misc |= ORB_SET_DATA_SIZE(sg_count); sbp2util_packet_dump(scatter_gather_element, (sizeof(struct sbp2_unrestricted_page_table)) * sg_count, "sbp2 s/g list", command->sge_dma); /* * Byte swap page tables if necessary */ sbp2util_cpu_to_be32_buffer(scatter_gather_element, (sizeof(struct sbp2_unrestricted_page_table)) * sg_count); } } /* * Byte swap command ORB if necessary */ sbp2util_cpu_to_be32_buffer(command_orb, sizeof(struct sbp2_command_orb)); /* * Put our scsi command in the command ORB */ memset(command_orb->cdb, 0, 12); memcpy(command_orb->cdb, scsi_cmd, COMMAND_SIZE(*scsi_cmd)); return(0);}/* * This function is called in order to begin a regular SBP-2 command. */static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id, struct sbp2_command_info *command){ struct sbp2scsi_host_info *hi = scsi_id->hi; struct sbp2_command_orb *command_orb = &command->command_orb; struct node_entry *ne = scsi_id->ne; u64 addr; outstanding_orb_incr; SBP2_ORB_DEBUG("sending command orb %p, total orbs = %x", command_orb, global_outstanding_command_orbs); pci_dma_sync_single_for_device(hi->host->pdev, command->command_orb_dma, sizeof(struct sbp2_command_orb), PCI_DMA_BIDIRECTIONAL); pci_dma_sync_single_for_device(hi->host->pdev, command->sge_dma, sizeof(command->scatter_gather_element), PCI_DMA_BIDIRECTIONAL); /* * Check to see if there are any previous orbs to use */ if (scsi_id->last_orb == NULL) { quadlet_t data[2]; /* * Ok, let's write to the target's management agent register */ addr = scsi_id->sbp2_command_block_agent_addr + SBP2_ORB_POINTER_OFFSET; data[0] = ORB_SET_NODE_ID(hi->host->node_id); data[1] = command->command_orb_dma; sbp2util_cpu_to_be32_buffer(data, 8); SBP2_ORB_DEBUG("write command agent, command orb %p", command_orb); if (sbp2util_node_write_no_wait(ne, addr, data, 8) < 0) { SBP2_ERR("sbp2util_node_write_no_wait failed.\n"); return -EIO; } SBP2_ORB_DEBUG("write command agent complete"); scsi_id->last_orb = command_orb; scsi_id->last_orb_dma = command->command_orb_dma; } else { quadlet_t data; /* * We have an orb already sent (maybe or maybe not * processed) that we can append this orb to. So do so, * and ring the doorbell. Have to be very careful * modifying these next orb pointers, as they are accessed * both by the sbp2 device and us. */ scsi_id->last_orb->next_ORB_lo = cpu_to_be32(command->command_orb_dma); /* Tells hardware that this pointer is valid */ scsi_id->last_orb->next_ORB_hi = 0x0; pci_dma_sync_single_for_device(hi->host->pdev, scsi_id->last_orb_dma, sizeof(struct sbp2_command_orb), PCI_DMA_BIDIRECTIONAL); /* * Ring the doorbell */ data = cpu_to_be32(command->command_orb_dma); addr = scsi_id->sbp2_command_block_agent_addr + SBP2_DOORBELL_OFFSET; SBP2_ORB_DEBUG("ring doorbell, command orb %p", command_orb); if (sbp2util_node_write_no_wait(ne, addr, &data, 4) < 0) { SBP2_ERR("sbp2util_node_write_no_wait failed"); return(-EIO); } scsi_id->last_orb = command_orb; scsi_id->last_orb_dma = command->command_orb_dma; } return(0);}/* * This function is called in order to begin a regular SBP-2 command. */static int sbp2_send_command(struct scsi_id_instance_data *scsi_id, Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)){ unchar *cmd = (unchar *) SCpnt->cmnd; unsigned int request_bufflen = SCpnt->request_bufflen; struct sbp2_command_info *command; SBP2_DEBUG("sbp2_send_command");#if (CONFIG_IEEE1394_SBP2_DEBUG >= 2) || defined(CONFIG_IEEE1394_SBP2_PACKET_DUMP) printk("[scsi command]\n "); print_command (cmd);#endif SBP2_DEBUG("SCSI transfer size = %x", request_bufflen); SBP2_DEBUG("SCSI s/g elements = %x", (unsigned int)SCpnt->use_sg); /* * Allocate a command orb and s/g structure */ command = sbp2util_allocate_command_orb(scsi_id, SCpnt, done); if (!command) { return(-EIO); } /* * The scsi stack sends down a request_bufflen which does not match the * length field in the scsi cdb. This causes some sbp2 devices to * reject this inquiry command. Fix the request_bufflen. */ if (*cmd == INQUIRY) { if (force_inquiry_hack || scsi_id->workarounds & SBP2_BREAKAGE_INQUIRY_HACK) request_bufflen = cmd[4] = 0x24; else request_bufflen = cmd[4]; } /* * Now actually fill in the comamnd orb and sbp2 s/g list */ sbp2_create_command_orb(scsi_id, command, cmd, SCpnt->use_sg, request_bufflen, SCpnt->request_buffer, SCpnt->sc_data_direction); /* * Update our cdb if necessary (to handle sbp2 RBC command set * differences). This is where the command set hacks go! =) */ sbp2_check_sbp2_command(scsi_id, command->command_orb.cdb); sbp2util_packet_dump(&command->command_orb, sizeof(struct sbp2_command_orb), "sbp2 command orb", command->command_orb_dma); /* * Initialize status fifo */ memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block)); /* * Link up the orb, and ring the doorbell if needed */ sbp2_link_orb_command(scsi_id, command); return(0);}/* * This function deals with command set differences between Linux scsi * command set and sbp2 RBC command set. */static void sbp2_check_sbp2_command(struct scsi_id_instance_data *scsi_id, unchar *cmd){ unchar new_cmd[16]; u8 device_type = SBP2_DEVICE_TYPE (scsi_id->sbp2_device_type_and_lun); SBP2_DEBUG("sbp2_check_sbp2_command"); switch (*cmd) { case READ_6: if (sbp2_command_conversion_device_type(device_type)) { SBP2_DEBUG("Convert READ_6 to READ_10"); /* * Need to turn read_6 into read_10 */ new_cmd[0] = 0x28; new_cmd[1] = (cmd[1] & 0xe0); new_cmd[2] = 0x0; new_cmd[3] = (cmd[1] & 0x1f); new_cmd[4] = cmd[2]; new_cmd[5] = cmd[3]; new_cmd[6] = 0x0; new_cmd[7] = 0x0; new_cmd[8] = cmd[4]; new_cmd[9] = cmd[5]; memcpy(cmd, new_cmd, 10); } break; case WRITE_6: if (sbp2_command_conversion_device_type(device_type)) { SBP2_DEBUG("Convert WRITE_6 to WRITE_10"); /* * Need to turn write_6 into write_10 */ new_cmd[0] = 0x2a; new_cmd[1] = (cmd[1] & 0xe0); new_cmd[2] = 0x0; new_cmd[3] = (cmd[1] & 0x1f); new_cmd[4] = cmd[2]; new_cmd[5] = cmd[3]; new_cmd[6] = 0x0; new_cmd[7] = 0x0; new_cmd[8] = cmd[4]; new_cmd[9] = cmd[5]; memcpy(cmd, new_cmd, 10); } break; case MODE_SENSE:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -