欢迎来到虫虫下载站 | 资源下载 资源专辑 关于我们
虫虫下载站

stex.c

linux 内核源代码
C
第 1 页 / 共 3 页
字号:
	n_elem = scsi_dma_map(cmd);	if (n_elem < 0)		return 0;	stex_internal_copy(cmd, src, &cp_len, n_elem, ST_TO_CMD);	scsi_dma_unmap(cmd);	return cp_len == count;}static void stex_controller_info(struct st_hba *hba, struct st_ccb *ccb){	struct st_frame *p;	size_t count = sizeof(struct st_frame);	p = hba->copy_buffer;	stex_internal_copy(ccb->cmd, p, &count, ccb->sg_count, ST_FROM_CMD);	memset(p->base, 0, sizeof(u32)*6);	*(unsigned long *)(p->base) = pci_resource_start(hba->pdev, 0);	p->rom_addr = 0;	p->drv_ver.major = ST_VER_MAJOR;	p->drv_ver.minor = ST_VER_MINOR;	p->drv_ver.oem = ST_OEM;	p->drv_ver.build = ST_BUILD_VER;	p->bus = hba->pdev->bus->number;	p->slot = hba->pdev->devfn;	p->irq_level = 0;	p->irq_vec = hba->pdev->irq;	p->id = hba->pdev->vendor << 16 | hba->pdev->device;	p->subid =		hba->pdev->subsystem_vendor << 16 | hba->pdev->subsystem_device;	stex_internal_copy(ccb->cmd, p, &count, ccb->sg_count, ST_TO_CMD);}static voidstex_send_cmd(struct st_hba *hba, struct req_msg *req, u16 tag){	req->tag = cpu_to_le16(tag);	req->task_attr = TASK_ATTRIBUTE_SIMPLE;	req->task_manage = 0; /* not supported yet */	hba->ccb[tag].req = req;	hba->out_req_cnt++;	writel(hba->req_head, hba->mmio_base + IMR0);	writel(MU_INBOUND_DOORBELL_REQHEADCHANGED, hba->mmio_base + IDBL);	readl(hba->mmio_base + IDBL); /* flush */}static intstex_slave_alloc(struct scsi_device *sdev){	/* Cheat: usually extracted from Inquiry data */	sdev->tagged_supported = 1;	scsi_activate_tcq(sdev, sdev->host->can_queue);	return 0;}static intstex_slave_config(struct scsi_device *sdev){	sdev->use_10_for_rw = 1;	sdev->use_10_for_ms = 1;	sdev->timeout = 60 * HZ;	sdev->tagged_supported = 1;	return 0;}static voidstex_slave_destroy(struct scsi_device *sdev){	scsi_deactivate_tcq(sdev, 1);}static intstex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)){	struct st_hba *hba;	struct Scsi_Host *host;	unsigned int id,lun;	struct req_msg *req;	u16 tag;	host = cmd->device->host;	id = cmd->device->id;	lun = cmd->device->lun;	hba = (struct st_hba *) &host->hostdata[0];	switch (cmd->cmnd[0]) {	case MODE_SENSE_10:	{		static char ms10_caching_page[12] =			{ 0, 0x12, 0, 0, 0, 0, 0, 0, 0x8, 0xa, 0x4, 0 };		unsigned char page;		page = cmd->cmnd[2] & 0x3f;		if (page == 0x8 || page == 0x3f) {			stex_direct_copy(cmd, ms10_caching_page,					sizeof(ms10_caching_page));			cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;			done(cmd);		} else			stex_invalid_field(cmd, done);		return 0;	}	case REPORT_LUNS:		/*		 * The shasta firmware does not report actual luns in the		 * target, so fail the command to force sequential lun scan.		 * Also, the console device does not support this command.		 */		if (hba->cardtype == st_shasta || id == host->max_id - 1) {			stex_invalid_field(cmd, done);			return 0;		}		break;	case TEST_UNIT_READY:		if (id == host->max_id - 1) {			cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;			done(cmd);			return 0;		}		break;	case INQUIRY:		if (id != host->max_id - 1)			break;		if (lun == 0 && (cmd->cmnd[1] & INQUIRY_EVPD) == 0) {			stex_direct_copy(cmd, console_inq_page,				sizeof(console_inq_page));			cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;			done(cmd);		} else			stex_invalid_field(cmd, done);		return 0;	case PASSTHRU_CMD:		if (cmd->cmnd[1] == PASSTHRU_GET_DRVVER) {			struct st_drvver ver;			ver.major = ST_VER_MAJOR;			ver.minor = ST_VER_MINOR;			ver.oem = ST_OEM;			ver.build = ST_BUILD_VER;			ver.signature[0] = PASSTHRU_SIGNATURE;			ver.console_id = host->max_id - 1;			ver.host_no = hba->host->host_no;			cmd->result = stex_direct_copy(cmd, &ver, sizeof(ver)) ?				DID_OK << 16 | COMMAND_COMPLETE << 8 :				DID_ERROR << 16 | COMMAND_COMPLETE << 8;			done(cmd);			return 0;		}	default:		break;	}	cmd->scsi_done = done;	tag = cmd->request->tag;	if (unlikely(tag >= host->can_queue))		return SCSI_MLQUEUE_HOST_BUSY;	req = stex_alloc_req(hba);	req->lun = lun;	req->target = id;	/* cdb */	memcpy(req->cdb, cmd->cmnd, STEX_CDB_LENGTH);	hba->ccb[tag].cmd = cmd;	hba->ccb[tag].sense_bufflen = SCSI_SENSE_BUFFERSIZE;	hba->ccb[tag].sense_buffer = cmd->sense_buffer;	hba->ccb[tag].req_type = 0;	if (cmd->sc_data_direction != DMA_NONE)		stex_map_sg(hba, req, &hba->ccb[tag]);	stex_send_cmd(hba, req, tag);	return 0;}static void stex_scsi_done(struct st_ccb *ccb){	struct scsi_cmnd *cmd = ccb->cmd;	int result;	if (ccb->srb_status == SRB_STATUS_SUCCESS ||  ccb->srb_status == 0) {		result = ccb->scsi_status;		switch (ccb->scsi_status) {		case SAM_STAT_GOOD:			result |= DID_OK << 16 | COMMAND_COMPLETE << 8;			break;		case SAM_STAT_CHECK_CONDITION:			result |= DRIVER_SENSE << 24;			break;		case SAM_STAT_BUSY:			result |= DID_BUS_BUSY << 16 | COMMAND_COMPLETE << 8;			break;		default:			result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;			break;		}	}	else if (ccb->srb_status & SRB_SEE_SENSE)		result = DRIVER_SENSE << 24 | SAM_STAT_CHECK_CONDITION;	else switch (ccb->srb_status) {		case SRB_STATUS_SELECTION_TIMEOUT:			result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;			break;		case SRB_STATUS_BUSY:			result = DID_BUS_BUSY << 16 | COMMAND_COMPLETE << 8;			break;		case SRB_STATUS_INVALID_REQUEST:		case SRB_STATUS_ERROR:		default:			result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;			break;	}	cmd->result = result;	cmd->scsi_done(cmd);}static void stex_copy_data(struct st_ccb *ccb,	struct status_msg *resp, unsigned int variable){	size_t count = variable;	if (resp->scsi_status != SAM_STAT_GOOD) {		if (ccb->sense_buffer != NULL)			memcpy(ccb->sense_buffer, resp->variable,				min(variable, ccb->sense_bufflen));		return;	}	if (ccb->cmd == NULL)		return;	stex_internal_copy(ccb->cmd,		resp->variable, &count, ccb->sg_count, ST_TO_CMD);}static void stex_ys_commands(struct st_hba *hba,	struct st_ccb *ccb, struct status_msg *resp){	size_t count;	if (ccb->cmd->cmnd[0] == MGT_CMD &&		resp->scsi_status != SAM_STAT_CHECK_CONDITION) {		scsi_set_resid(ccb->cmd, scsi_bufflen(ccb->cmd) -			le32_to_cpu(*(__le32 *)&resp->variable[0]));		return;	}	if (resp->srb_status != 0)		return;	/* determine inquiry command status by DeviceTypeQualifier */	if (ccb->cmd->cmnd[0] == INQUIRY &&		resp->scsi_status == SAM_STAT_GOOD) {		ST_INQ *inq_data;		count = STEX_EXTRA_SIZE;		stex_internal_copy(ccb->cmd, hba->copy_buffer,			&count, ccb->sg_count, ST_FROM_CMD);		inq_data = (ST_INQ *)hba->copy_buffer;		if (inq_data->DeviceTypeQualifier != 0)			ccb->srb_status = SRB_STATUS_SELECTION_TIMEOUT;		else			ccb->srb_status = SRB_STATUS_SUCCESS;	}}static void stex_mu_intr(struct st_hba *hba, u32 doorbell){	void __iomem *base = hba->mmio_base;	struct status_msg *resp;	struct st_ccb *ccb;	unsigned int size;	u16 tag;	if (!(doorbell & MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED))		return;	/* status payloads */	hba->status_head = readl(base + OMR1);	if (unlikely(hba->status_head >= MU_STATUS_COUNT)) {		printk(KERN_WARNING DRV_NAME "(%s): invalid status head\n",			pci_name(hba->pdev));		return;	}	/*	 * it's not a valid status payload if:	 * 1. there are no pending requests(e.g. during init stage)	 * 2. there are some pending requests, but the controller is in	 *     reset status, and its type is not st_yosemite	 * firmware of st_yosemite in reset status will return pending requests	 * to driver, so we allow it to pass	 */	if (unlikely(hba->out_req_cnt <= 0 ||			(hba->mu_status == MU_STATE_RESETTING &&			 hba->cardtype != st_yosemite))) {		hba->status_tail = hba->status_head;		goto update_status;	}	while (hba->status_tail != hba->status_head) {		resp = stex_get_status(hba);		tag = le16_to_cpu(resp->tag);		if (unlikely(tag >= hba->host->can_queue)) {			printk(KERN_WARNING DRV_NAME				"(%s): invalid tag\n", pci_name(hba->pdev));			continue;		}		ccb = &hba->ccb[tag];		if (hba->wait_ccb == ccb)			hba->wait_ccb = NULL;		if (unlikely(ccb->req == NULL)) {			printk(KERN_WARNING DRV_NAME				"(%s): lagging req\n", pci_name(hba->pdev));			hba->out_req_cnt--;			continue;		}		size = resp->payload_sz * sizeof(u32); /* payload size */		if (unlikely(size < sizeof(*resp) - STATUS_VAR_LEN ||			size > sizeof(*resp))) {			printk(KERN_WARNING DRV_NAME "(%s): bad status size\n",				pci_name(hba->pdev));		} else {			size -= sizeof(*resp) - STATUS_VAR_LEN; /* copy size */			if (size)				stex_copy_data(ccb, resp, size);		}		ccb->srb_status = resp->srb_status;		ccb->scsi_status = resp->scsi_status;		if (likely(ccb->cmd != NULL)) {			if (hba->cardtype == st_yosemite)				stex_ys_commands(hba, ccb, resp);			if (unlikely(ccb->cmd->cmnd[0] == PASSTHRU_CMD &&				ccb->cmd->cmnd[1] == PASSTHRU_GET_ADAPTER))				stex_controller_info(hba, ccb);			scsi_dma_unmap(ccb->cmd);			stex_scsi_done(ccb);			hba->out_req_cnt--;		} else if (ccb->req_type & PASSTHRU_REQ_TYPE) {			hba->out_req_cnt--;			if (ccb->req_type & PASSTHRU_REQ_NO_WAKEUP) {				ccb->req_type = 0;				continue;			}			ccb->req_type = 0;			if (waitqueue_active(&hba->waitq))				wake_up(&hba->waitq);		}	}update_status:	writel(hba->status_head, base + IMR1);	readl(base + IMR1); /* flush */}static irqreturn_t stex_intr(int irq, void *__hba){	struct st_hba *hba = __hba;	void __iomem *base = hba->mmio_base;	u32 data;	unsigned long flags;	int handled = 0;	spin_lock_irqsave(hba->host->host_lock, flags);	data = readl(base + ODBL);	if (data && data != 0xffffffff) {		/* clear the interrupt */		writel(data, base + ODBL);		readl(base + ODBL); /* flush */		stex_mu_intr(hba, data);		handled = 1;	}	spin_unlock_irqrestore(hba->host->host_lock, flags);	return IRQ_RETVAL(handled);}static int stex_handshake(struct st_hba *hba){	void __iomem *base = hba->mmio_base;	struct handshake_frame *h;	dma_addr_t status_phys;	u32 data;	unsigned long before;	if (readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE) {		writel(MU_INBOUND_DOORBELL_HANDSHAKE, base + IDBL);		readl(base + IDBL);		before = jiffies;		while (readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE) {			if (time_after(jiffies, before + MU_MAX_DELAY * HZ)) {				printk(KERN_ERR DRV_NAME					"(%s): no handshake signature\n",					pci_name(hba->pdev));				return -1;			}			rmb();			msleep(1);		}	}	udelay(10);	data = readl(base + OMR1);	if ((data & 0xffff0000) == MU_HANDSHAKE_SIGNATURE_HALF) {		data &= 0x0000ffff;		if (hba->host->can_queue > data)			hba->host->can_queue = data;	}	h = (struct handshake_frame *)(hba->dma_mem + MU_REQ_BUFFER_SIZE);	h->rb_phy = cpu_to_le32(hba->dma_handle);	h->rb_phy_hi = cpu_to_le32((hba->dma_handle >> 16) >> 16);	h->req_sz = cpu_to_le16(sizeof(struct req_msg));	h->req_cnt = cpu_to_le16(MU_REQ_COUNT);	h->status_sz = cpu_to_le16(sizeof(struct status_msg));	h->status_cnt = cpu_to_le16(MU_STATUS_COUNT);	stex_gettime(&h->hosttime);	h->partner_type = HMU_PARTNER_TYPE;	if (hba->dma_size > STEX_BUFFER_SIZE) {		h->extra_offset = cpu_to_le32(STEX_BUFFER_SIZE);		h->extra_size = cpu_to_le32(ST_ADDITIONAL_MEM);	} else		h->extra_offset = h->extra_size = 0;	status_phys = hba->dma_handle + MU_REQ_BUFFER_SIZE;	writel(status_phys, base + IMR0);	readl(base + IMR0);	writel((status_phys >> 16) >> 16, base + IMR1);	readl(base + IMR1);	writel((status_phys >> 16) >> 16, base + OMR0); /* old fw compatible */	readl(base + OMR0);	writel(MU_INBOUND_DOORBELL_HANDSHAKE, base + IDBL);	readl(base + IDBL); /* flush */	udelay(10);	before = jiffies;	while (readl(base + OMR0) != MU_HANDSHAKE_SIGNATURE) {		if (time_after(jiffies, before + MU_MAX_DELAY * HZ)) {			printk(KERN_ERR DRV_NAME				"(%s): no signature after handshake frame\n",				pci_name(hba->pdev));			return -1;		}		rmb();		msleep(1);	}	writel(0, base + IMR0);

⌨️ 快捷键说明

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