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

📄 sym_glue.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family  * of PCI-SCSI IO processors. * * Copyright (C) 1999-2001  Gerard Roudier <groudier@free.fr> * Copyright (c) 2003-2005  Matthew Wilcox <matthew@wil.cx> * * This driver is derived from the Linux sym53c8xx driver. * Copyright (C) 1998-2000  Gerard Roudier * * The sym53c8xx driver is derived from the ncr53c8xx driver that had been  * a port of the FreeBSD ncr driver to Linux-1.2.13. * * The original ncr driver has been written for 386bsd and FreeBSD by *         Wolfgang Stanglmeier        <wolf@cologne.de> *         Stefan Esser                <se@mi.Uni-Koeln.de> * Copyright (C) 1994  Wolfgang Stanglmeier * * Other major contributions: * * NVRAM detection and reading. * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk> * *----------------------------------------------------------------------------- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */#include <linux/ctype.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/spinlock.h>#include <scsi/scsi.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_device.h>#include <scsi/scsi_transport.h>#include "sym_glue.h"#include "sym_nvram.h"#define NAME53C		"sym53c"#define NAME53C8XX	"sym53c8xx"/* SPARC just has to be different ... */#ifdef __sparc__#define IRQ_FMT "%s"#define IRQ_PRM(x) __irq_itoa(x)#else#define IRQ_FMT "%d"#define IRQ_PRM(x) (x)#endifstruct sym_driver_setup sym_driver_setup = SYM_LINUX_DRIVER_SETUP;unsigned int sym_debug_flags = 0;static char *excl_string;static char *safe_string;module_param_named(cmd_per_lun, sym_driver_setup.max_tag, ushort, 0);module_param_string(tag_ctrl, sym_driver_setup.tag_ctrl, 100, 0);module_param_named(burst, sym_driver_setup.burst_order, byte, 0);module_param_named(led, sym_driver_setup.scsi_led, byte, 0);module_param_named(diff, sym_driver_setup.scsi_diff, byte, 0);module_param_named(irqm, sym_driver_setup.irq_mode, byte, 0);module_param_named(buschk, sym_driver_setup.scsi_bus_check, byte, 0);module_param_named(hostid, sym_driver_setup.host_id, byte, 0);module_param_named(verb, sym_driver_setup.verbose, byte, 0);module_param_named(debug, sym_debug_flags, uint, 0);module_param_named(settle, sym_driver_setup.settle_delay, byte, 0);module_param_named(nvram, sym_driver_setup.use_nvram, byte, 0);module_param_named(excl, excl_string, charp, 0);module_param_named(safe, safe_string, charp, 0);MODULE_PARM_DESC(cmd_per_lun, "The maximum number of tags to use by default");MODULE_PARM_DESC(tag_ctrl, "More detailed control over tags per LUN");MODULE_PARM_DESC(burst, "Maximum burst.  0 to disable, 255 to read from registers");MODULE_PARM_DESC(led, "Set to 1 to enable LED support");MODULE_PARM_DESC(diff, "0 for no differential mode, 1 for BIOS, 2 for always, 3 for not GPIO3");MODULE_PARM_DESC(irqm, "0 for open drain, 1 to leave alone, 2 for totem pole");MODULE_PARM_DESC(buschk, "0 to not check, 1 for detach on error, 2 for warn on error");MODULE_PARM_DESC(hostid, "The SCSI ID to use for the host adapters");MODULE_PARM_DESC(verb, "0 for minimal verbosity, 1 for normal, 2 for excessive");MODULE_PARM_DESC(debug, "Set bits to enable debugging");MODULE_PARM_DESC(settle, "Settle delay in seconds.  Default 3");MODULE_PARM_DESC(nvram, "Option currently not used");MODULE_PARM_DESC(excl, "List ioport addresses here to prevent controllers from being attached");MODULE_PARM_DESC(safe, "Set other settings to a \"safe mode\"");MODULE_LICENSE("GPL");MODULE_VERSION(SYM_VERSION);MODULE_AUTHOR("Matthew Wilcox <matthew@wil.cx>");MODULE_DESCRIPTION("NCR, Symbios and LSI 8xx and 1010 PCI SCSI adapters");static void sym2_setup_params(void){	char *p = excl_string;	int xi = 0;	while (p && (xi < 8)) {		char *next_p;		int val = (int) simple_strtoul(p, &next_p, 0);		sym_driver_setup.excludes[xi++] = val;		p = next_p;	}	if (safe_string) {		if (*safe_string == 'y') {			sym_driver_setup.max_tag = 0;			sym_driver_setup.burst_order = 0;			sym_driver_setup.scsi_led = 0;			sym_driver_setup.scsi_diff = 1;			sym_driver_setup.irq_mode = 0;			sym_driver_setup.scsi_bus_check = 2;			sym_driver_setup.host_id = 7;			sym_driver_setup.verbose = 2;			sym_driver_setup.settle_delay = 10;			sym_driver_setup.use_nvram = 1;		} else if (*safe_string != 'n') {			printk(KERN_WARNING NAME53C8XX "Ignoring parameter %s"					" passed to safe option", safe_string);		}	}}/* * We used to try to deal with 64-bit BARs here, but don't any more. * There are many parts of this driver which would need to be modified * to handle a 64-bit base address, including scripts.  I'm uncomfortable * with making those changes when I have no way of testing it, so I'm * just going to disable it. * * Note that some machines (eg HP rx8620 and Superdome) have bus addresses * below 4GB and physical addresses above 4GB.  These will continue to work. */static int __devinitpci_get_base_address(struct pci_dev *pdev, int index, unsigned long *basep){	u32 tmp;	unsigned long base;#define PCI_BAR_OFFSET(index) (PCI_BASE_ADDRESS_0 + (index<<2))	pci_read_config_dword(pdev, PCI_BAR_OFFSET(index++), &tmp);	base = tmp;	if ((tmp & 0x7) == PCI_BASE_ADDRESS_MEM_TYPE_64) {		pci_read_config_dword(pdev, PCI_BAR_OFFSET(index++), &tmp);		if (tmp > 0) {			dev_err(&pdev->dev,				"BAR %d is 64-bit, disabling\n", index - 1);			base = 0;		}	}	if ((base & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {		base &= PCI_BASE_ADDRESS_IO_MASK;	} else {		base &= PCI_BASE_ADDRESS_MEM_MASK;	}	*basep = base;	return index;#undef PCI_BAR_OFFSET}static struct scsi_transport_template *sym2_transport_template = NULL;/* *  Used by the eh thread to wait for command completion. *  It is allocated on the eh thread stack. */struct sym_eh_wait {	struct completion done;	struct timer_list timer;	void (*old_done)(struct scsi_cmnd *);	int to_do;	int timed_out;};/* *  Driver private area in the SCSI command structure. */struct sym_ucmd {		/* Override the SCSI pointer structure */	dma_addr_t data_mapping;	u_char	data_mapped;	struct sym_eh_wait *eh_wait;};#define SYM_UCMD_PTR(cmd)  ((struct sym_ucmd *)(&(cmd)->SCp))#define SYM_SOFTC_PTR(cmd) sym_get_hcb(cmd->device->host)static void __unmap_scsi_data(struct pci_dev *pdev, struct scsi_cmnd *cmd){	int dma_dir = cmd->sc_data_direction;	switch(SYM_UCMD_PTR(cmd)->data_mapped) {	case 2:		pci_unmap_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);		break;	case 1:		pci_unmap_single(pdev, SYM_UCMD_PTR(cmd)->data_mapping,				 cmd->request_bufflen, dma_dir);		break;	}	SYM_UCMD_PTR(cmd)->data_mapped = 0;}static dma_addr_t __map_scsi_single_data(struct pci_dev *pdev, struct scsi_cmnd *cmd){	dma_addr_t mapping;	int dma_dir = cmd->sc_data_direction;	mapping = pci_map_single(pdev, cmd->request_buffer,				 cmd->request_bufflen, dma_dir);	if (mapping) {		SYM_UCMD_PTR(cmd)->data_mapped  = 1;		SYM_UCMD_PTR(cmd)->data_mapping = mapping;	}	return mapping;}static int __map_scsi_sg_data(struct pci_dev *pdev, struct scsi_cmnd *cmd){	int use_sg;	int dma_dir = cmd->sc_data_direction;	use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);	if (use_sg > 0) {		SYM_UCMD_PTR(cmd)->data_mapped  = 2;		SYM_UCMD_PTR(cmd)->data_mapping = use_sg;	}	return use_sg;}#define unmap_scsi_data(np, cmd)	\		__unmap_scsi_data(np->s.device, cmd)#define map_scsi_single_data(np, cmd)	\		__map_scsi_single_data(np->s.device, cmd)#define map_scsi_sg_data(np, cmd)	\		__map_scsi_sg_data(np->s.device, cmd)/* *  Complete a pending CAM CCB. */void sym_xpt_done(struct sym_hcb *np, struct scsi_cmnd *cmd){	unmap_scsi_data(np, cmd);	cmd->scsi_done(cmd);}static void sym_xpt_done2(struct sym_hcb *np, struct scsi_cmnd *cmd, int cam_status){	sym_set_cam_status(cmd, cam_status);	sym_xpt_done(np, cmd);}/* *  Tell the SCSI layer about a BUS RESET. */void sym_xpt_async_bus_reset(struct sym_hcb *np){	printf_notice("%s: SCSI BUS has been reset.\n", sym_name(np));	np->s.settle_time = jiffies + sym_driver_setup.settle_delay * HZ;	np->s.settle_time_valid = 1;	if (sym_verbose >= 2)		printf_info("%s: command processing suspended for %d seconds\n",			    sym_name(np), sym_driver_setup.settle_delay);}/* *  Tell the SCSI layer about a BUS DEVICE RESET message sent. */void sym_xpt_async_sent_bdr(struct sym_hcb *np, int target){	printf_notice("%s: TARGET %d has been reset.\n", sym_name(np), target);}/* *  Choose the more appropriate CAM status if  *  the IO encountered an extended error. */static int sym_xerr_cam_status(int cam_status, int x_status){	if (x_status) {		if	(x_status & XE_PARITY_ERR)			cam_status = DID_PARITY;		else if	(x_status &(XE_EXTRA_DATA|XE_SODL_UNRUN|XE_SWIDE_OVRUN))			cam_status = DID_ERROR;		else if	(x_status & XE_BAD_PHASE)			cam_status = DID_ERROR;		else			cam_status = DID_ERROR;	}	return cam_status;}/* *  Build CAM result for a failed or auto-sensed IO. */void sym_set_cam_result_error(struct sym_hcb *np, struct sym_ccb *cp, int resid){	struct scsi_cmnd *cmd = cp->cmd;	u_int cam_status, scsi_status, drv_status;	drv_status  = 0;	cam_status  = DID_OK;	scsi_status = cp->ssss_status;	if (cp->host_flags & HF_SENSE) {		scsi_status = cp->sv_scsi_status;		resid = cp->sv_resid;		if (sym_verbose && cp->sv_xerr_status)			sym_print_xerr(cmd, cp->sv_xerr_status);		if (cp->host_status == HS_COMPLETE &&		    cp->ssss_status == S_GOOD &&		    cp->xerr_status == 0) {			cam_status = sym_xerr_cam_status(DID_OK,							 cp->sv_xerr_status);			drv_status = DRIVER_SENSE;			/*			 *  Bounce back the sense data to user.			 */			memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));			memcpy(cmd->sense_buffer, cp->sns_bbuf,			      min(sizeof(cmd->sense_buffer),				  (size_t)SYM_SNS_BBUF_LEN));#if 0			/*			 *  If the device reports a UNIT ATTENTION condition 			 *  due to a RESET condition, we should consider all 			 *  disconnect CCBs for this unit as aborted.			 */			if (1) {				u_char *p;				p  = (u_char *) cmd->sense_data;				if (p[0]==0x70 && p[2]==0x6 && p[12]==0x29)					sym_clear_tasks(np, DID_ABORT,							cp->target,cp->lun, -1);			}#endif		} else {			/*			 * Error return from our internal request sense.  This			 * is bad: we must clear the contingent allegiance			 * condition otherwise the device will always return			 * BUSY.  Use a big stick.			 */			sym_reset_scsi_target(np, cmd->device->id);			cam_status = DID_ERROR;		}	} else if (cp->host_status == HS_COMPLETE) 	/* Bad SCSI status */		cam_status = DID_OK;	else if (cp->host_status == HS_SEL_TIMEOUT)	/* Selection timeout */		cam_status = DID_NO_CONNECT;	else if (cp->host_status == HS_UNEXPECTED)	/* Unexpected BUS FREE*/		cam_status = DID_ERROR;	else {						/* Extended error */		if (sym_verbose) {			sym_print_addr(cmd, "COMMAND FAILED (%x %x %x).\n",				cp->host_status, cp->ssss_status,				cp->xerr_status);		}		/*		 *  Set the most appropriate value for CAM status.		 */		cam_status = sym_xerr_cam_status(DID_ERROR, cp->xerr_status);	}	cmd->resid = resid;	cmd->result = (drv_status << 24) + (cam_status << 16) + scsi_status;}/* *  Build the scatter/gather array for an I/O. */static int sym_scatter_no_sglist(struct sym_hcb *np, struct sym_ccb *cp, struct scsi_cmnd *cmd){	struct sym_tblmove *data = &cp->phys.data[SYM_CONF_MAX_SG-1];	int segment;	unsigned int len = cmd->request_bufflen;	if (len) {		dma_addr_t baddr = map_scsi_single_data(np, cmd);		if (baddr) {			if (len & 1) {				struct sym_tcb *tp = &np->target[cp->target];				if (tp->head.wval & EWS) {					len++;					cp->odd_byte_adjustment++;				}			}			cp->data_len = len;			sym_build_sge(np, data, baddr, len);			segment = 1;		} else {			segment = -2;		}	} else {		segment = 0;	}	return segment;}static int sym_scatter(struct sym_hcb *np, struct sym_ccb *cp, struct scsi_cmnd *cmd){	int segment;	int use_sg = (int) cmd->use_sg;	cp->data_len = 0;	if (!use_sg)		segment = sym_scatter_no_sglist(np, cp, cmd);	else if ((use_sg = map_scsi_sg_data(np, cmd)) > 0) {		struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;		struct sym_tcb *tp = &np->target[cp->target];		struct sym_tblmove *data;		if (use_sg > SYM_CONF_MAX_SG) {			unmap_scsi_data(np, cmd);			return -1;		}		data = &cp->phys.data[SYM_CONF_MAX_SG - use_sg];		for (segment = 0; segment < use_sg; segment++) {			dma_addr_t baddr = sg_dma_address(&scatter[segment]);			unsigned int len = sg_dma_len(&scatter[segment]);			if ((len & 1) && (tp->head.wval & EWS)) {				len++;				cp->odd_byte_adjustment++;			}			sym_build_sge(np, &data[segment], baddr, len);			cp->data_len += len;		}	} else {		segment = -2;	}	return segment;}/* *  Queue a SCSI command. */static int sym_queue_command(struct sym_hcb *np, struct scsi_cmnd *cmd){	struct scsi_device *sdev = cmd->device;	struct sym_tcb *tp;	struct sym_lcb *lp;	struct sym_ccb *cp;	int	order;	/*	 *  Minimal checkings, so that we will not 	 *  go outside our tables.	 */	if (sdev->id == np->myaddr) {		sym_xpt_done2(np, cmd, DID_NO_CONNECT);		return 0;	}	/*	 *  Retrieve the target descriptor.	 */	tp = &np->target[sdev->id];	/*	 *  Select tagged/untagged.	 */	lp = sym_lp(tp, sdev->lun);	order = (lp && lp->s.reqtags) ? M_SIMPLE_TAG : 0;	/*	 *  Queue the SCSI IO.	 */	cp = sym_get_ccb(np, cmd, order);	if (!cp)		return 1;	/* Means resource shortage */	sym_queue_scsiio(np, cmd, cp);	return 0;}/* *  Setup buffers and pointers that address the CDB. */static inline int sym_setup_cdb(struct sym_hcb *np, struct scsi_cmnd *cmd, struct sym_ccb *cp){	memcpy(cp->cdb_buf, cmd->cmnd, cmd->cmd_len);	cp->phys.cmd.addr = CCB_BA(cp, cdb_buf[0]);	cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len);	return 0;}/* *  Setup pointers that address the data and start the I/O. */int sym_setup_data_and_start(struct sym_hcb *np, struct scsi_cmnd *cmd, struct sym_ccb *cp){	int dir;	struct sym_tcb *tp = &np->target[cp->target];	struct sym_lcb *lp = sym_lp(tp, cp->lun);	/*	 *  Build the CDB.	 */	if (sym_setup_cdb(np, cmd, cp))		goto out_abort;	/*	 *  No direction means no data.	 */	dir = cmd->sc_data_direction;	if (dir != DMA_NONE) {		cp->segments = sym_scatter(np, cp, cmd);		if (cp->segments < 0) {			sym_set_cam_status(cmd, DID_ERROR);			goto out_abort;		}	} else {		cp->data_len = 0;		cp->segments = 0;	}	/*	 *  Set data pointers.	 */	sym_setup_data_pointers(np, cp, dir);	/*	 *  When `#ifed 1', the code below makes the driver 	 *  panic on the first attempt to write to a SCSI device.	 *  It is the first test we want to do after a driver 	 *  change that does not seem obviously safe. :)	 */

⌨️ 快捷键说明

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