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

📄 sbp2.c

📁 Ieee1394驱动实现
💻 C
📖 第 1 页 / 共 5 页
字号:
		data[0] = ORB_SET_NODE_ID(hi->host->node_id);
		data[1] = cmd->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.
		 */
		dma_sync_single_for_cpu(hi->host->device.parent, last_orb_dma,
					sizeof(struct sbp2_command_orb),
					DMA_TO_DEVICE);
		last_orb->next_ORB_lo = cpu_to_be32(cmd->command_orb_dma);
		wmb();
		/* Tells hardware that this pointer is valid */
		last_orb->next_ORB_hi = 0;
		dma_sync_single_for_device(hi->host->device.parent,
					   last_orb_dma,
					   sizeof(struct sbp2_command_orb),
					   DMA_TO_DEVICE);
		addr += SBP2_DOORBELL_OFFSET;
		data[0] = 0;
		length = 4;
	}
	lu->last_orb = &cmd->command_orb;
	lu->last_orb_dma = cmd->command_orb_dma;
	spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);

	if (sbp2util_node_write_no_wait(lu->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(lu->shost);
		PREPARE_WORK(&lu->protocol_work,
			     last_orb ? sbp2util_write_doorbell:
					sbp2util_write_orb_pointer);
		schedule_work(&lu->protocol_work);
	}
}

static int sbp2_send_command(struct sbp2_lu *lu, struct scsi_cmnd *SCpnt,
			     void (*done)(struct scsi_cmnd *))
{
	unchar *scsi_cmd = (unchar *)SCpnt->cmnd;
	unsigned int request_bufflen = SCpnt->request_bufflen;
	struct sbp2_command_info *cmd;

	cmd = sbp2util_allocate_command_orb(lu, SCpnt, done);
	if (!cmd)
		return -EIO;

	sbp2_create_command_orb(lu, cmd, scsi_cmd, SCpnt->use_sg,
				request_bufflen, SCpnt->request_buffer,
				SCpnt->sc_data_direction);
	sbp2_link_orb_command(lu, cmd);

	return 0;
}

/*
 * Translates SBP-2 status into SCSI sense data for check conditions
 */
static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status,
					      unchar *sense_data)
{
	/* OK, it's pretty ugly... ;-) */
	sense_data[0] = 0x70;
	sense_data[1] = 0x0;
	sense_data[2] = sbp2_status[9];
	sense_data[3] = sbp2_status[12];
	sense_data[4] = sbp2_status[13];
	sense_data[5] = sbp2_status[14];
	sense_data[6] = sbp2_status[15];
	sense_data[7] = 10;
	sense_data[8] = sbp2_status[16];
	sense_data[9] = sbp2_status[17];
	sense_data[10] = sbp2_status[18];
	sense_data[11] = sbp2_status[19];
	sense_data[12] = sbp2_status[10];
	sense_data[13] = sbp2_status[11];
	sense_data[14] = sbp2_status[20];
	sense_data[15] = sbp2_status[21];

	return sbp2_status[8] & 0x3f;
}

static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid,
				    int destid, quadlet_t *data, u64 addr,
				    size_t length, u16 fl)
{
	struct sbp2_fwhost_info *hi;
	struct sbp2_lu *lu = NULL, *lu_tmp;
	struct scsi_cmnd *SCpnt = NULL;
	struct sbp2_status_block *sb;
	u32 scsi_status = SBP2_SCSI_STATUS_GOOD;
	struct sbp2_command_info *cmd;
	unsigned long flags;

	if (unlikely(length < 8 || length > sizeof(struct sbp2_status_block))) {
		SBP2_ERR("Wrong size of status block");
		return RCODE_ADDRESS_ERROR;
	}
	if (unlikely(!host)) {
		SBP2_ERR("host is NULL - this is bad!");
		return RCODE_ADDRESS_ERROR;
	}
	hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
	if (unlikely(!hi)) {
		SBP2_ERR("host info is NULL - this is bad!");
		return RCODE_ADDRESS_ERROR;
	}

	/* Find the unit which wrote the status. */
	list_for_each_entry(lu_tmp, &hi->logical_units, lu_list) {
		if (lu_tmp->ne->nodeid == nodeid &&
		    lu_tmp->status_fifo_addr == addr) {
			lu = lu_tmp;
			break;
		}
	}
	if (unlikely(!lu)) {
		SBP2_ERR("lu is NULL - device is gone?");
		return RCODE_ADDRESS_ERROR;
	}

	/* Put response into lu status fifo buffer. The first two bytes
	 * come in big endian bit order. Often the target writes only a
	 * truncated status block, minimally the first two quadlets. The rest
	 * is implied to be zeros. */
	sb = &lu->status_block;
	memset(sb->command_set_dependent, 0, sizeof(sb->command_set_dependent));
	memcpy(sb, data, length);
	sbp2util_be32_to_cpu_buffer(sb, 8);

	/* Ignore unsolicited status. Handle command ORB status. */
	if (unlikely(STATUS_GET_SRC(sb->ORB_offset_hi_misc) == 2))
		cmd = NULL;
	else
		cmd = sbp2util_find_command_for_orb(lu, sb->ORB_offset_lo);
	if (cmd) {
		dma_sync_single_for_cpu(hi->host->device.parent,
					cmd->command_orb_dma,
					sizeof(struct sbp2_command_orb),
					DMA_TO_DEVICE);
		dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma,
					sizeof(cmd->scatter_gather_element),
					DMA_TO_DEVICE);
		/* Grab SCSI command pointers and check status. */
		/*
		 * FIXME: If the src field in the status is 1, the ORB DMA must
		 * not be reused until status for a subsequent ORB is received.
		 */
		SCpnt = cmd->Current_SCpnt;
		spin_lock_irqsave(&lu->cmd_orb_lock, flags);
		sbp2util_mark_command_completed(lu, cmd);
		spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);

		if (SCpnt) {
			u32 h = sb->ORB_offset_hi_misc;
			u32 r = STATUS_GET_RESP(h);

			if (r != RESP_STATUS_REQUEST_COMPLETE) {
				SBP2_INFO("resp 0x%x, sbp_status 0x%x",
					  r, STATUS_GET_SBP_STATUS(h));
				scsi_status =
					r == RESP_STATUS_TRANSPORT_FAILURE ?
					SBP2_SCSI_STATUS_BUSY :
					SBP2_SCSI_STATUS_COMMAND_TERMINATED;
			}

			if (STATUS_GET_LEN(h) > 1)
				scsi_status = sbp2_status_to_sense_data(
					(unchar *)sb, SCpnt->sense_buffer);

			if (STATUS_TEST_DEAD(h))
                                sbp2_agent_reset(lu, 0);
		}

		/* Check here to see if there are no commands in-use. If there
		 * are none, we know that the fetch agent left the active state
		 * _and_ that we did not reactivate it yet. Therefore clear
		 * last_orb so that next time we write directly to the
		 * ORB_POINTER register. That way the fetch agent does not need
		 * to refetch the next_ORB. */
		spin_lock_irqsave(&lu->cmd_orb_lock, flags);
		if (list_empty(&lu->cmd_orb_inuse))
			lu->last_orb = NULL;
		spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);

	} else {
		/* It's probably status after a management request. */
		if ((sb->ORB_offset_lo == lu->reconnect_orb_dma) ||
		    (sb->ORB_offset_lo == lu->login_orb_dma) ||
		    (sb->ORB_offset_lo == lu->query_logins_orb_dma) ||
		    (sb->ORB_offset_lo == lu->logout_orb_dma)) {
			lu->access_complete = 1;
			wake_up_interruptible(&sbp2_access_wq);
		}
	}

	if (SCpnt)
		sbp2scsi_complete_command(lu, scsi_status, SCpnt,
					  cmd->Current_done);
	return RCODE_COMPLETE;
}

/**************************************
 * SCSI interface related section
 **************************************/

static int sbp2scsi_queuecommand(struct scsi_cmnd *SCpnt,
				 void (*done)(struct scsi_cmnd *))
{
	struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0];
	struct sbp2_fwhost_info *hi;
	int result = DID_NO_CONNECT << 16;

	if (unlikely(!sbp2util_node_is_available(lu)))
		goto done;

	hi = lu->hi;

	if (unlikely(!hi)) {
		SBP2_ERR("sbp2_fwhost_info is NULL - this is bad!");
		goto done;
	}

	/* Multiple units are currently represented to the SCSI core as separate
	 * targets, not as one target with multiple LUs. Therefore return
	 * selection time-out to any IO directed at non-zero LUNs. */
	if (unlikely(SCpnt->device->lun))
		goto done;

	if (unlikely(!hpsb_node_entry_valid(lu->ne))) {
		SBP2_ERR("Bus reset in progress - rejecting command");
		result = DID_BUS_BUSY << 16;
		goto done;
	}

	/* Bidirectional commands are not yet implemented,
	 * and unknown transfer direction not handled. */
	if (unlikely(SCpnt->sc_data_direction == DMA_BIDIRECTIONAL)) {
		SBP2_ERR("Cannot handle DMA_BIDIRECTIONAL - rejecting command");
		result = DID_ERROR << 16;
		goto done;
	}

	if (sbp2_send_command(lu, SCpnt, done)) {
		SBP2_ERR("Error sending SCSI command");
		sbp2scsi_complete_command(lu,
					  SBP2_SCSI_STATUS_SELECTION_TIMEOUT,
					  SCpnt, done);
	}
	return 0;

done:
	SCpnt->result = result;
	done(SCpnt);
	return 0;
}

static void sbp2scsi_complete_all_commands(struct sbp2_lu *lu, u32 status)
{
	struct sbp2_fwhost_info *hi = lu->hi;
	struct list_head *lh;
	struct sbp2_command_info *cmd;
	unsigned long flags;

	spin_lock_irqsave(&lu->cmd_orb_lock, flags);
	while (!list_empty(&lu->cmd_orb_inuse)) {
		lh = lu->cmd_orb_inuse.next;
		cmd = list_entry(lh, struct sbp2_command_info, list);
		dma_sync_single_for_cpu(hi->host->device.parent,
				        cmd->command_orb_dma,
					sizeof(struct sbp2_command_orb),
					DMA_TO_DEVICE);
		dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma,
					sizeof(cmd->scatter_gather_element),
					DMA_TO_DEVICE);
		sbp2util_mark_command_completed(lu, cmd);
		if (cmd->Current_SCpnt) {
			cmd->Current_SCpnt->result = status << 16;
			cmd->Current_done(cmd->Current_SCpnt);
		}
	}
	spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);

	return;
}

/*
 * Complete a regular SCSI command. Can be called in atomic context.
 */
static void sbp2scsi_complete_command(struct sbp2_lu *lu, u32 scsi_status,
				      struct scsi_cmnd *SCpnt,
				      void (*done)(struct scsi_cmnd *))
{
	if (!SCpnt) {
		SBP2_ERR("SCpnt is NULL");
		return;
	}

	switch (scsi_status) {
	case SBP2_SCSI_STATUS_GOOD:
		SCpnt->result = DID_OK << 16;
		break;

	case SBP2_SCSI_STATUS_BUSY:
		SBP2_ERR("SBP2_SCSI_STATUS_BUSY");
		SCpnt->result = DID_BUS_BUSY << 16;
		break;

	case SBP2_SCSI_STATUS_CHECK_CONDITION:
		SCpnt->result = CHECK_CONDITION << 1 | DID_OK << 16;
		break;

	case SBP2_SCSI_STATUS_SELECTION_TIMEOUT:
		SBP2_ERR("SBP2_SCSI_STATUS_SELECTION_TIMEOUT");
		SCpnt->result = DID_NO_CONNECT << 16;
		scsi_print_command(SCpnt);
		break;

	case SBP2_SCSI_STATUS_CONDITION_MET:
	case SBP2_SCSI_STATUS_RESERVATION_CONFLICT:
	case SBP2_SCSI_STATUS_COMMAND_TERMINATED:
		SBP2_ERR("Bad SCSI status = %x", scsi_status);
		SCpnt->result = DID_ERROR << 16;
		scsi_print_command(SCpnt);
		break;

	default:
		SBP2_ERR("Unsupported SCSI status = %x", scsi_status);
		SCpnt->result = DID_ERROR << 16;
	}

	/* If a bus reset is in progress and there was an error, complete
	 * the command as busy so that it will get retried. */
	if (!hpsb_node_entry_valid(lu->ne)
	    && (scsi_status != SBP2_SCSI_STATUS_GOOD)) {
		SBP2_ERR("Completing command with busy (bus reset)");
		SCpnt->result = DID_BUS_BUSY << 16;
	}

	/* Tell the SCSI stack that we're done with this command. */
	done(SCpnt);
}

static int sbp2scsi_slave_alloc(struct scsi_device *sdev)
{
	struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0];

	lu->sdev = sdev;
	sdev->allow_restart = 1;

	if (lu->workarounds & SBP2_WORKAROUND_INQUIRY_36)
		sdev->inquiry_len = 36;
	return 0;
}

static int sbp2scsi_slave_configure(struct scsi_device *sdev)
{
	struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0];

	sdev->use_10_for_rw = 1;

	if (sdev->type == TYPE_ROM)
		sdev->use_10_for_ms = 1;
	if (sdev->type == TYPE_DISK &&
	    lu->workarounds & SBP2_WORKAROUND_MODE_SENSE_8)
		sdev->skip_ms_page_8 = 1;
	if (lu->workarounds & SBP2_WORKAROUND_FIX_CAPACITY)
		sdev->fix_capacity = 1;
	return 0;
}

static void sbp2scsi_slave_destroy(struct scsi_device *sdev)
{
	((struct sbp2_lu *)sdev->host->hostdata[0])->sdev = NULL;
	return;
}

/*
 * Called by scsi stack when something has really gone wrong.
 * Usually called when a command has timed-out for some reason.
 */
static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
{
	struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0];
	struct sbp2_fwhost_info *hi = lu->hi;
	struct sbp2_command_info *cmd;
	unsigned long flags;

	SBP2_INFO("aborting sbp2 command");
	scsi_print_command(SCpnt);

	if (sbp2util_node_is_available(lu)) {
		sbp2_agent_reset(lu, 1);

		/* Return a matching command structure to the free pool. */
		spin_lock_irqsave(&lu->cmd_orb_lock, flags);
		cmd = sbp2util_find_command_for_SCpnt(lu, SCpnt);
		if (cmd) {
			dma_sync_single_for_cpu(hi->host->device.parent,
					cmd->command_orb_dma,
					sizeof(struct sbp2_command_orb),
					DMA_TO_DEVICE);
			dma_sync_single_for_cpu(hi->host

⌨️ 快捷键说明

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