⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sbp2a.c

📁 IEE1394 SBP-2 linux 驱动原码
💻 C
📖 第 1 页 / 共 5 页
字号:
		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;
	u64 addr;
	int retval;
	unsigned long flags;

	SBP2_DEBUG_ENTER();

	cancel_delayed_work(&scsi_id->protocol_work);
	if (wait)
		flush_scheduled_work();

	data = ntohl(SBP2_AGENT_RESET_DATA);
	addr = scsi_id->sbp2_command_block_agent_addr + SBP2_AGENT_RESET_OFFSET;

	if (wait)
		retval = hpsb_node_write(scsi_id->ne, addr, &data, 4);
	else
		retval = sbp2util_node_write_no_wait(scsi_id->ne, addr, &data, 4);

	if (retval < 0) {
		SBP2_ERR("hpsb_node_write failed.\n");
		return -EIO;
	}

	/*
	 * Need to make sure orb pointer is written on next command
	 */
	spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
	scsi_id->last_orb = NULL;
	spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);

	return 0;
}

static void sbp2_prep_command_orb_sg(struct sbp2_command_orb *orb,
				     struct sbp2scsi_host_info *hi,
				     struct sbp2_command_info *command,
				     unsigned int scsi_use_sg,
				     struct scatterlist *sgpnt,
				     u32 orb_direction,
				     enum dma_data_direction dma_dir)
{
	command->dma_dir = dma_dir;
	orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id);
	orb->misc |= ORB_SET_DIRECTION(orb_direction);

	/* 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_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");

		orb->data_descriptor_lo = command->cmd_dma;
		orb->misc |= ORB_SET_DATA_SIZE(command->dma_size);

	} else {
		struct sbp2_unrestricted_page_table *sg_element =
					&command->scatter_gather_element[0];
		u32 sg_count, sg_len;
		dma_addr_t sg_addr;
		int i, 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->sge_buffer = sgpnt;

		/* use page tables (s/g) */
		orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1);
		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) {
				sg_element[sg_count].segment_base_lo = sg_addr;
				if (sg_len > SBP2_MAX_SG_ELEMENT_LENGTH) {
					sg_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 {
					sg_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 */
		orb->misc |= ORB_SET_DATA_SIZE(sg_count);

		sbp2util_packet_dump(sg_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(sg_element,
					    (sizeof(struct sbp2_unrestricted_page_table)) *
					    sg_count);
	}
}

static void sbp2_prep_command_orb_no_sg(struct sbp2_command_orb *orb,
					struct sbp2scsi_host_info *hi,
					struct sbp2_command_info *command,
					struct scatterlist *sgpnt,
					u32 orb_direction,
					unsigned int scsi_request_bufflen,
					void *scsi_request_buffer,
					enum dma_data_direction dma_dir)
{
	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);
	orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id);
	orb->misc |= ORB_SET_DIRECTION(orb_direction);

	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) {

		orb->data_descriptor_lo = command->cmd_dma;
		orb->misc |= ORB_SET_DATA_SIZE(scsi_request_bufflen);

	} else {
		struct sbp2_unrestricted_page_table *sg_element =
			&command->scatter_gather_element[0];
		u32 sg_count, sg_len;
		dma_addr_t sg_addr;

		/*
		 * Need to turn this into page tables, since the
		 * buffer is too large.
		 */
		orb->data_descriptor_lo = command->sge_dma;

		/* Use page tables (s/g) */
		orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1);

		/*
		 * 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) {
			sg_element[sg_count].segment_base_lo = sg_addr;
			if (sg_len > SBP2_MAX_SG_ELEMENT_LENGTH) {
				sg_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 {
				sg_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 */
		orb->misc |= ORB_SET_DATA_SIZE(sg_count);

		sbp2util_packet_dump(sg_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(sg_element,
					    (sizeof(struct sbp2_unrestricted_page_table)) *
					     sg_count);
	}
}

/*
 * This function is called to create the actual command orb and s/g list
 * out of the scsi command itself.
 */
static void sbp2_create_command_orb(struct scsi_id_instance_data *scsi_id,
				    struct sbp2_command_info *command,
				    unchar *scsi_cmd,
				    unsigned int scsi_use_sg,
				    unsigned int scsi_request_bufflen,
				    void *scsi_request_buffer,
				    enum dma_data_direction dma_dir)
{
	struct sbp2scsi_host_info *hi = scsi_id->hi;
	struct scatterlist *sgpnt = (struct scatterlist *)scsi_request_buffer;
	struct sbp2_command_orb *command_orb = &command->command_orb;
	u32 orb_direction;

	/*
	 * Set-up our command ORB..
	 *
	 * NOTE: We're doing unrestricted page tables (s/g), as this is
	 * best performance (at least with the devices I have). This means
	 * that data_size becomes the number of s/g elements, and
	 * page_size should be zero (for unrestricted).
	 */
	command_orb->next_ORB_hi = ORB_SET_NULL_PTR(1);
	command_orb->next_ORB_lo = 0x0;
	command_orb->misc = ORB_SET_MAX_PAYLOAD(scsi_id->max_payload_size);
	command_orb->misc |= ORB_SET_SPEED(scsi_id->speed_code);
	command_orb->misc |= ORB_SET_NOTIFY(1);	/* Notify us when complete */

	if (dma_dir == DMA_NONE)
		orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER;
	else if (dma_dir == DMA_TO_DEVICE && scsi_request_bufflen)
		orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA;
	else if (dma_dir == DMA_FROM_DEVICE && scsi_request_bufflen)
		orb_direction = ORB_DIRECTION_READ_FROM_MEDIA;
	else {
		SBP2_WARN("Falling back to DMA_NONE");
		orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER;
	}

	/* Set-up our pagetable stuff */
	if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) {
		SBP2_DEBUG("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");
		sbp2_prep_command_orb_sg(command_orb, hi, command, scsi_use_sg,
					 sgpnt, orb_direction, dma_dir);
	} else {
		SBP2_DEBUG("No scatter/gather");
		sbp2_prep_command_orb_no_sg(command_orb, hi, command, sgpnt,
					    orb_direction, scsi_request_bufflen,
					    scsi_request_buffer, dma_dir);
	}

	/* 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));
}

/*
 * This function is called in order to begin a regular SBP-2 command.
 */
static void 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 sbp2_command_orb *last_orb;
	dma_addr_t last_orb_dma;
	u64 addr = scsi_id->sbp2_command_block_agent_addr;
	quadlet_t data[2];
	size_t length;
	unsigned long flags;

	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_TODEVICE);
	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
	 */
	spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
	last_orb = scsi_id->last_orb;
	last_orb_dma = scsi_id->last_orb_dma;
	if (!last_orb) {
		/*
		 * last_orb == NULL means: We know that the target's fetch agent
		 * is not active right now.
		 */
		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);
		length = 8;
	} else {
		/*
		 * last_orb != NULL means: We know that the target's fetch agent
		 * is (very probably) not dead or in reset state right now.
		 * We have an ORB already sent that we can append a new one to.
		 * The target's fetch agent may or may not have read this
		 * previous ORB yet.
		 */
		pci_dma_sync_single_for_cpu(hi->host->pdev, last_orb_dma,
					    sizeof(struct sbp2_command_orb),
					    PCI_DMA_TODEVICE);
		last_orb->next_ORB_lo = cpu_to_be32(command->command_orb_dma);
		wmb();
		/* Tells hardware that this pointer is valid */
		last_orb->next_ORB_hi = 0;
		pci_dma_sync_single_for_device(hi->host->pdev, last_orb_dma,
					       sizeof(struct sbp2_command_orb),
					       PCI_DMA_TODEVICE);
		addr += SBP2_DOORBELL_OFFSET;
		data[0] = 0;
		length = 4;
	}
	scsi_id->last_orb = command_orb;
	scsi_id->last_orb_dma = command->command_orb_dma;
	spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);

	SBP2_ORB_DEBUG("write to %s register, command orb %p",
			last_orb ? "DOORBELL" : "ORB_POINTER", command_orb);
	if (sbp2util_node_write_no_wait(scsi_id->ne, addr, data, length)) {
		/*
		 * sbp2util_node_write_no_wait failed. We certainly ran out
		 * of transaction labels, perhaps just because there were no
		 * context switches which gave khpsbpkt a chance to collect
		 * free tlabels. Try again in non-atomic context. If necessary,
		 * the workqueue job will sleep to guaranteedly get a tlabel.
		 * We do not accept new commands until the job is over.
		 */
		scsi_block_requests(scsi_id->scsi_host);
		PREPARE_WORK(&scsi_id->protocol_work,
			     last_orb ? sbp2util_write_doorbell:
					sbp2util_write_orb_pointer,
			     scsi_id);
		sche

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -