ibmmca.c

来自「linux 内核源代码」· C语言 代码 · 共 1,772 行 · 第 1/5 页

C
1,772
字号
	unsigned char _get_scsi[16][8];	/* used only when checking logical devices: */	int _local_checking_phase_flag;	/* report received interrupt: */	int _got_interrupt;	/* report termination-status of SCSI-command: */	int _stat_result;	/* reset status (used only when doing reset): */	int _reset_status;	/* code of the last SCSI command (needed for panic info): */	int _last_scsi_command[MAX_LOG_DEV + 1];	/* identifier of the last SCSI-command type */	int _last_scsi_type[MAX_LOG_DEV + 1];	/* last blockcount */	int _last_scsi_blockcount[MAX_LOG_DEV + 1];	/* last locgical block address */	unsigned long _last_scsi_logical_block[MAX_LOG_DEV + 1];	/* Counter that points on the next reassignable ldn for dynamical	   remapping. The default value is 7, that is the first reassignable	   number in the list at boottime: */	int _next_ldn;	/* Statistics-structure for this IBM-SCSI-host: */	struct Driver_Statistics _IBM_DS;	/* This hostadapters pos-registers pos2 until pos6 */	unsigned int _pos[8];	/* assign a special variable, that contains dedicated info about the	   adaptertype */	int _special;	/* connector size on the MCA bus */	int _connector_size;	/* synchronous SCSI transfer rate bitpattern */	int _adapter_speed;};/* macros to access host data structure */#define subsystem_pun(h) ((h)->this_id)#define subsystem_maxid(h) ((h)->max_id)#define ld(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_ld)#define get_ldn(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_get_ldn)#define get_scsi(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_get_scsi)#define local_checking_phase_flag(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_local_checking_phase_flag)#define got_interrupt(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_got_interrupt)#define stat_result(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_stat_result)#define reset_status(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_reset_status)#define last_scsi_command(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_command)#define last_scsi_type(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_type)#define last_scsi_blockcount(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_blockcount)#define last_scsi_logical_block(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_logical_block)#define last_scsi_type(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_last_scsi_type)#define next_ldn(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_next_ldn)#define IBM_DS(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_IBM_DS)#define special(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_special)#define subsystem_connector_size(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_connector_size)#define adapter_speed(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_adapter_speed)#define pos2(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[2])#define pos3(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[3])#define pos4(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[4])#define pos5(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[5])#define pos6(h) (((struct ibmmca_hostdata *) (h)->hostdata)->_pos[6])/* Define a arbitrary number as subsystem-marker-type. This number is, as   described in the ANSI-SCSI-standard, not occupied by other device-types. */#define TYPE_IBM_SCSI_ADAPTER   0x2F/* Define 0xFF for no device type, because this type is not defined within   the ANSI-SCSI-standard, therefore, it can be used and should not cause any   harm. */#define TYPE_NO_DEVICE          0xFF/* define medium-changer. If this is not defined previously, e.g. Linux   2.0.x, define this type here. */#ifndef TYPE_MEDIUM_CHANGER#define TYPE_MEDIUM_CHANGER     0x08#endif/* define possible operations for the immediate_assign command */#define SET_LDN        0#define REMOVE_LDN     1/* ldn which is used to probe the SCSI devices */#define PROBE_LDN      0/* reset status flag contents */#define IM_RESET_NOT_IN_PROGRESS         0#define IM_RESET_IN_PROGRESS             1#define IM_RESET_FINISHED_OK             2#define IM_RESET_FINISHED_FAIL           3#define IM_RESET_NOT_IN_PROGRESS_NO_INT  4#define IM_RESET_FINISHED_OK_NO_INT      5/* define undefined SCSI-command */#define NO_SCSI                  0xffff/*-----------------------------------------------------------------------*//* if this is nonzero, ibmmcascsi option has been passed to the kernel */static int io_port[IM_MAX_HOSTS] = { 0, 0, 0, 0, 0, 0, 0, 0 };static int scsi_id[IM_MAX_HOSTS] = { 7, 7, 7, 7, 7, 7, 7, 7 };/* fill module-parameters only, when this define is present.   (that is kernel version 2.1.x) */#if defined(MODULE)static char *boot_options = NULL;module_param(boot_options, charp, 0);module_param_array(io_port, int, NULL, 0);module_param_array(scsi_id, int, NULL, 0);MODULE_LICENSE("GPL");#endif/*counter of concurrent disk read/writes, to turn on/off disk led */static int disk_rw_in_progress = 0;static unsigned int pos[8];	/* whole pos register-line for diagnosis *//* Taking into account the additions, made by ZP Gu. * This selects now the preset value from the configfile and * offers the 'normal' commandline option to be accepted */#ifdef CONFIG_IBMMCA_SCSI_ORDER_STANDARDstatic char ibm_ansi_order = 1;#elsestatic char ibm_ansi_order = 0;#endifstatic void issue_cmd(struct Scsi_Host *, unsigned long, unsigned char);static void internal_done(Scsi_Cmnd * cmd);static void check_devices(struct Scsi_Host *, int);static int immediate_assign(struct Scsi_Host *, unsigned int, unsigned int, unsigned int, unsigned int);static int immediate_feature(struct Scsi_Host *, unsigned int, unsigned int);#ifdef CONFIG_IBMMCA_SCSI_DEV_RESETstatic int immediate_reset(struct Scsi_Host *, unsigned int);#endifstatic int device_inquiry(struct Scsi_Host *, int);static int read_capacity(struct Scsi_Host *, int);static int get_pos_info(struct Scsi_Host *);static char *ti_p(int);static char *ti_l(int);static char *ibmrate(unsigned int, int);static int probe_display(int);static int probe_bus_mode(struct Scsi_Host *);static int device_exists(struct Scsi_Host *, int, int *, int *);static int option_setup(char *);/* local functions needed for proc_info */static int ldn_access_load(struct Scsi_Host *, int);static int ldn_access_total_read_write(struct Scsi_Host *);static irqreturn_t interrupt_handler(int irq, void *dev_id){	unsigned int intr_reg;	unsigned int cmd_result;	unsigned int ldn;	unsigned long flags;	Scsi_Cmnd *cmd;	int lastSCSI;	struct device *dev = dev_id;	struct Scsi_Host *shpnt = dev_get_drvdata(dev);	spin_lock_irqsave(shpnt->host_lock, flags);	if(!(inb(IM_STAT_REG(shpnt)) & IM_INTR_REQUEST)) {		spin_unlock_irqrestore(shpnt->host_lock, flags);		return IRQ_NONE;	}	/* the reset-function already did all the job, even ints got	   renabled on the subsystem, so just return */	if ((reset_status(shpnt) == IM_RESET_NOT_IN_PROGRESS_NO_INT) || (reset_status(shpnt) == IM_RESET_FINISHED_OK_NO_INT)) {		reset_status(shpnt) = IM_RESET_NOT_IN_PROGRESS;		spin_unlock_irqrestore(shpnt->host_lock, flags);		return IRQ_HANDLED;	}	/*must wait for attention reg not busy, then send EOI to subsystem */	while (1) {		if (!(inb(IM_STAT_REG(shpnt)) & IM_BUSY))			break;		cpu_relax();	}	/*get command result and logical device */	intr_reg = (unsigned char) (inb(IM_INTR_REG(shpnt)));	cmd_result = intr_reg & 0xf0;	ldn = intr_reg & 0x0f;	/* get the last_scsi_command here */	lastSCSI = last_scsi_command(shpnt)[ldn];	outb(IM_EOI | ldn, IM_ATTN_REG(shpnt));		/*these should never happen (hw fails, or a local programming bug) */	if (!global_command_error_excuse) {		switch (cmd_result) {			/* Prevent from Ooopsing on error to show the real reason */		case IM_ADAPTER_HW_FAILURE:		case IM_SOFTWARE_SEQUENCING_ERROR:		case IM_CMD_ERROR:			printk(KERN_ERR "IBM MCA SCSI: Fatal Subsystem ERROR!\n");			printk(KERN_ERR "              Last cmd=0x%x, ena=%x, len=", lastSCSI, ld(shpnt)[ldn].scb.enable);			if (ld(shpnt)[ldn].cmd)				printk("%ld/%ld,", (long) (scsi_bufflen(ld(shpnt)[ldn].cmd)), (long) (ld(shpnt)[ldn].scb.sys_buf_length));			else				printk("none,");			if (ld(shpnt)[ldn].cmd)				printk("Blocksize=%d", ld(shpnt)[ldn].scb.u2.blk.length);			else				printk("Blocksize=none");			printk(", host=%p, ldn=0x%x\n", shpnt, ldn);			if (ld(shpnt)[ldn].cmd) {				printk(KERN_ERR "Blockcount=%d/%d\n", last_scsi_blockcount(shpnt)[ldn], ld(shpnt)[ldn].scb.u2.blk.count);				printk(KERN_ERR "Logical block=%lx/%lx\n", last_scsi_logical_block(shpnt)[ldn], ld(shpnt)[ldn].scb.u1.log_blk_adr);			}			printk(KERN_ERR "Reason given: %s\n", (cmd_result == IM_ADAPTER_HW_FAILURE) ? "HARDWARE FAILURE" : (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR) ? "SOFTWARE SEQUENCING ERROR" : (cmd_result == IM_CMD_ERROR) ? "COMMAND ERROR" : "UNKNOWN");			/* if errors appear, enter this section to give detailed info */			printk(KERN_ERR "IBM MCA SCSI: Subsystem Error-Status follows:\n");			printk(KERN_ERR "              Command Type................: %x\n", last_scsi_type(shpnt)[ldn]);			printk(KERN_ERR "              Attention Register..........: %x\n", inb(IM_ATTN_REG(shpnt)));			printk(KERN_ERR "              Basic Control Register......: %x\n", inb(IM_CTR_REG(shpnt)));			printk(KERN_ERR "              Interrupt Status Register...: %x\n", intr_reg);			printk(KERN_ERR "              Basic Status Register.......: %x\n", inb(IM_STAT_REG(shpnt)));			if ((last_scsi_type(shpnt)[ldn] == IM_SCB) || (last_scsi_type(shpnt)[ldn] == IM_LONG_SCB)) {				printk(KERN_ERR "              SCB-Command.................: %x\n", ld(shpnt)[ldn].scb.command);				printk(KERN_ERR "              SCB-Enable..................: %x\n", ld(shpnt)[ldn].scb.enable);				printk(KERN_ERR "              SCB-logical block address...: %lx\n", ld(shpnt)[ldn].scb.u1.log_blk_adr);				printk(KERN_ERR "              SCB-system buffer address...: %lx\n", ld(shpnt)[ldn].scb.sys_buf_adr);				printk(KERN_ERR "              SCB-system buffer length....: %lx\n", ld(shpnt)[ldn].scb.sys_buf_length);				printk(KERN_ERR "              SCB-tsb address.............: %lx\n", ld(shpnt)[ldn].scb.tsb_adr);				printk(KERN_ERR "              SCB-Chain address...........: %lx\n", ld(shpnt)[ldn].scb.scb_chain_adr);				printk(KERN_ERR "              SCB-block count.............: %x\n", ld(shpnt)[ldn].scb.u2.blk.count);				printk(KERN_ERR "              SCB-block length............: %x\n", ld(shpnt)[ldn].scb.u2.blk.length);			}			printk(KERN_ERR "              Send this report to the maintainer.\n");			panic("IBM MCA SCSI: Fatal error message from the subsystem (0x%X,0x%X)!\n", lastSCSI, cmd_result);			break;		}	} else {		/* The command error handling is made silent, but we tell the		 * calling function, that there is a reported error from the		 * adapter. */		switch (cmd_result) {		case IM_ADAPTER_HW_FAILURE:		case IM_SOFTWARE_SEQUENCING_ERROR:		case IM_CMD_ERROR:			global_command_error_excuse = CMD_FAIL;			break;		default:			global_command_error_excuse = 0;			break;		}	}	/* if no panic appeared, increase the interrupt-counter */	IBM_DS(shpnt).total_interrupts++;	/*only for local checking phase */	if (local_checking_phase_flag(shpnt)) {		stat_result(shpnt) = cmd_result;		got_interrupt(shpnt) = 1;		reset_status(shpnt) = IM_RESET_FINISHED_OK;		last_scsi_command(shpnt)[ldn] = NO_SCSI;		spin_unlock_irqrestore(shpnt->host_lock, flags);		return IRQ_HANDLED;	}	/* handling of commands coming from upper level of scsi driver */	if (last_scsi_type(shpnt)[ldn] == IM_IMM_CMD) {		/* verify ldn, and may handle rare reset immediate command */		if ((reset_status(shpnt) == IM_RESET_IN_PROGRESS) && (last_scsi_command(shpnt)[ldn] == IM_RESET_IMM_CMD)) {			if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) {				disk_rw_in_progress = 0;				PS2_DISK_LED_OFF();				reset_status(shpnt) = IM_RESET_FINISHED_FAIL;			} else {				/*reset disk led counter, turn off disk led */				disk_rw_in_progress = 0;				PS2_DISK_LED_OFF();				reset_status(shpnt) = IM_RESET_FINISHED_OK;			}			stat_result(shpnt) = cmd_result;			last_scsi_command(shpnt)[ldn] = NO_SCSI;			last_scsi_type(shpnt)[ldn] = 0;			spin_unlock_irqrestore(shpnt->host_lock, flags);			return IRQ_HANDLED;		} else if (last_scsi_command(shpnt)[ldn] == IM_ABORT_IMM_CMD) {			/* react on SCSI abort command */#ifdef IM_DEBUG_PROBE			printk("IBM MCA SCSI: Interrupt from SCSI-abort.\n");#endif			disk_rw_in_progress = 0;			PS2_DISK_LED_OFF();			cmd = ld(shpnt)[ldn].cmd;			ld(shpnt)[ldn].cmd = NULL;			if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE)				cmd->result = DID_NO_CONNECT << 16;			else				cmd->result = DID_ABORT << 16;			stat_result(shpnt) = cmd_result;			last_scsi_command(shpnt)[ldn] = NO_SCSI;			last_scsi_type(shpnt)[ldn] = 0;			if (cmd->scsi_done)				(cmd->scsi_done) (cmd);	/* should be the internal_done */			spin_unlock_irqrestore(shpnt->host_lock, flags);			return IRQ_HANDLED;		} else {			disk_rw_in_progress = 0;			PS2_DISK_LED_OFF();			reset_status(shpnt) = IM_RESET_FINISHED_OK;			stat_result(shpnt) = cmd_result;			last_scsi_command(shpnt)[ldn] = NO_SCSI;			spin_unlock_irqrestore(shpnt->host_lock, flags);			return IRQ_HANDLED;		}	}	last_scsi_command(shpnt)[ldn] = NO_SCSI;	last_scsi_type(shpnt)[ldn] = 0;	cmd = ld(shpnt)[ldn].cmd;	ld(shpnt)[ldn].cmd = NULL;#ifdef IM_DEBUG_TIMEOUT	if (cmd) {		if ((cmd->target == TIMEOUT_PUN) && (cmd->device->lun == TIMEOUT_LUN)) {			spin_unlock_irqsave(shpnt->host_lock, flags);			printk("IBM MCA SCSI: Ignoring interrupt from pun=%x, lun=%x.\n", cmd->target, cmd->device->lun);			return IRQ_HANDLED;		}	}#endif	/*if no command structure, just return, else clear cmd */	if (!cmd)	{		spin_unlock_irqrestore(shpnt->host_lock, flags);		return IRQ_HANDLED;	}#ifdef IM_DEBUG_INT	printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n", cmd->cmnd[0], intr_reg, ld(shpnt)[ldn].tsb.dev_status, ld(shpnt)[ldn].tsb.cmd_status, ld(shpnt)[ldn].tsb.dev_error, ld(shpnt)[ldn].tsb.cmd_error);#endif	/*if this is end of media read/write, may turn off PS/2 disk led */	if ((ld(shpnt)[ldn].device_type != TYPE_NO_LUN) && (ld(shpnt)[ldn].device_type != TYPE_NO_DEVICE)) {		/* only access this, if there was a valid device addressed */		if (--disk_rw_in_progress == 0)			PS2_DISK_LED_OFF();	}	/* IBM describes the status-mask to be 0x1e, but this is not conform	 * with SCSI-definition, I suppose, the reason for it is that IBM	 * adapters do not support CMD_TERMINATED, TASK_SET_FULL and	 * ACA_ACTIVE as returning statusbyte information. (ML) */	if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) {		cmd->result = (unsigned char) (ld(shpnt)[ldn].tsb.dev_status & 0x1e);		IBM_DS(shpnt).total_errors++;	} else		cmd->result = 0;	/* write device status into cmd->result, and call done function */	if (lastSCSI == NO_SCSI) {	/* unexpected interrupt :-( */		cmd->result |= DID_BAD_INTR << 16;		printk("IBM MCA SCSI: WARNING - Interrupt from non-pending SCSI-command!\n");	} else			/* things went right :-) */		cmd->result |= DID_OK << 16;	if (cmd->scsi_done)		(cmd->scsi_done) (cmd);	spin_unlock_irqrestore(shpnt->host_lock, flags);	return IRQ_HANDLED;}

⌨️ 快捷键说明

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