3w-9xxx.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,986 行 · 第 1/5 页

C
1,986
字号
/*   3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux.   Written By: Adam Radford <linuxraid@amcc.com>   Copyright (C) 2004 Applied Micro Circuits Corporation.   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; version 2 of the License.   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.   NO WARRANTY   THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR   CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT   LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is   solely responsible for determining the appropriateness of using and   distributing the Program and assumes all risks associated with its   exercise of rights under this Agreement, including but not limited to   the risks and costs of program errors, damage to or loss of data,   programs or equipment, and unavailability or interruption of operations.   DISCLAIMER OF LIABILITY   NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL   DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE   USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED   HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES   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   Bugs/Comments/Suggestions should be mailed to:   linuxraid@amcc.com   For more information, goto:   http://www.amcc.com   Note: This version of the driver does not contain a bundled firmware         image.   History   -------   2.26.02.000 - Driver cleanup for kernel submission.   2.26.02.001 - Replace schedule_timeout() calls with msleep().*/#include <linux/module.h>#include <linux/reboot.h>#include <linux/spinlock.h>#include <linux/interrupt.h>#include <linux/moduleparam.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/pci.h>#include <linux/time.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <scsi/scsi.h>#include <scsi/scsi_host.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_cmnd.h>#include "3w-9xxx.h"/* Globals */static const char *twa_driver_version="2.26.02.001";static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];static unsigned int twa_device_extension_count;static int twa_major = -1;extern struct timezone sys_tz;/* Module parameters */MODULE_AUTHOR ("AMCC");MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver");MODULE_LICENSE("GPL");/* Function prototypes */static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header);static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id);static char *twa_aen_severity_lookup(unsigned char severity_code);static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id);static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static int twa_chrdev_open(struct inode *inode, struct file *file);static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host);static void twa_free_request_id(TW_Device_Extension *tw_dev,int request_id);static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id);static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits, 			      u32 set_features, unsigned short current_fw_srl, 			      unsigned short current_fw_arch_id, 			      unsigned short current_fw_branch, 			      unsigned short current_fw_build, 			      unsigned short *fw_on_ctlr_srl, 			      unsigned short *fw_on_ctlr_arch_id, 			      unsigned short *fw_on_ctlr_branch, 			      unsigned short *fw_on_ctlr_build, 			      u32 *init_connect_result);static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length);static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds);static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds);static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal);static int twa_reset_device_extension(TW_Device_Extension *tw_dev);static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset);static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Apache *sglistarg);static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id);static char *twa_string_lookup(twa_message_type *table, unsigned int aen_code);static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id);/* Functions *//* Show some statistics about the card */static ssize_t twa_show_stats(struct class_device *class_dev, char *buf){	struct Scsi_Host *host = class_to_shost(class_dev);	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;	unsigned long flags = 0;	ssize_t len;	spin_lock_irqsave(tw_dev->host->host_lock, flags);	len = snprintf(buf, PAGE_SIZE, "Driver version: %s\n"		       "Current commands posted:   %4d\n"		       "Max commands posted:       %4d\n"		       "Current pending commands:  %4d\n"		       "Max pending commands:      %4d\n"		       "Last sgl length:           %4d\n"		       "Max sgl length:            %4d\n"		       "Last sector count:         %4d\n"		       "Max sector count:          %4d\n"		       "SCSI Host Resets:          %4d\n"		       "SCSI Aborts/Timeouts:      %4d\n"		       "AEN's:                     %4d\n", 		       twa_driver_version,		       tw_dev->posted_request_count,		       tw_dev->max_posted_request_count,		       tw_dev->pending_request_count,		       tw_dev->max_pending_request_count,		       tw_dev->sgl_entries,		       tw_dev->max_sgl_entries,		       tw_dev->sector_count,		       tw_dev->max_sector_count,		       tw_dev->num_resets,		       tw_dev->num_aborts,		       tw_dev->aen_count);	spin_unlock_irqrestore(tw_dev->host->host_lock, flags);	return len;} /* End twa_show_stats() *//* This function will set a devices queue depth */static ssize_t twa_store_queue_depth(struct device *dev, const char *buf, size_t count){	int queue_depth;	struct scsi_device *sdev = to_scsi_device(dev);	queue_depth = simple_strtoul(buf, NULL, 0);	if (queue_depth > TW_Q_LENGTH-2)		return -EINVAL;	scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);	return count;} /* End twa_store_queue_depth() *//* Create sysfs 'queue_depth' entry */static struct device_attribute twa_queue_depth_attr = {	.attr = {		.name =		"queue_depth",		.mode =		S_IRUSR | S_IWUSR,	},	.store = twa_store_queue_depth};/* Device attributes initializer */static struct device_attribute *twa_dev_attrs[] = {	&twa_queue_depth_attr,	NULL,};/* Create sysfs 'stats' entry */static struct class_device_attribute twa_host_stats_attr = {	.attr = {		.name = 	"stats",		.mode =		S_IRUGO,	},	.show = twa_show_stats};/* Host attributes initializer */static struct class_device_attribute *twa_host_attrs[] = {	&twa_host_stats_attr,	NULL,};/* File operations struct for character device */static struct file_operations twa_fops = {	.owner		= THIS_MODULE,	.ioctl		= twa_chrdev_ioctl,	.open		= twa_chrdev_open,	.release	= NULL};/* This function will complete an aen request from the isr */static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id){	TW_Command_Full *full_command_packet;	TW_Command *command_packet;	TW_Command_Apache_Header *header;	unsigned short aen;	int retval = 1;	header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];	tw_dev->posted_request_count--;	aen = header->status_block.error;	full_command_packet = tw_dev->command_packet_virt[request_id];	command_packet = &full_command_packet->command.oldcommand;	/* First check for internal completion of set param for time sync */	if (TW_OP_OUT(command_packet->opcode__sgloffset) == TW_OP_SET_PARAM) {		/* Keep reading the queue in case there are more aen's */		if (twa_aen_read_queue(tw_dev, request_id))			goto out2;	        else {			retval = 0;			goto out;		}	}	switch (aen) {	case TW_AEN_QUEUE_EMPTY:		/* Quit reading the queue if this is the last one */		break;	case TW_AEN_SYNC_TIME_WITH_HOST:		twa_aen_sync_time(tw_dev, request_id);		retval = 0;		goto out;	default:		twa_aen_queue_event(tw_dev, header);		/* If there are more aen's, keep reading the queue */		if (twa_aen_read_queue(tw_dev, request_id))			goto out2;		else {			retval = 0;			goto out;		}	}	retval = 0;out2:	tw_dev->state[request_id] = TW_S_COMPLETED;	twa_free_request_id(tw_dev, request_id);	clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);out:	return retval;} /* End twa_aen_complete() *//* This function will drain aen queue */static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset){	int request_id = 0;	char cdb[TW_MAX_CDB_LEN];	TW_SG_Apache sglist[1];	int finished = 0, count = 0;	TW_Command_Full *full_command_packet;	TW_Command_Apache_Header *header;	unsigned short aen;	int first_reset = 0, queue = 0, retval = 1;	if (no_check_reset)		first_reset = 0;	else		first_reset = 1;	full_command_packet = tw_dev->command_packet_virt[request_id];	memset(full_command_packet, 0, sizeof(TW_Command_Full));	/* Initialize cdb */	memset(&cdb, 0, TW_MAX_CDB_LEN);	cdb[0] = REQUEST_SENSE; /* opcode */	cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */	/* Initialize sglist */	memset(&sglist, 0, sizeof(TW_SG_Apache));	sglist[0].length = TW_SECTOR_SIZE;	sglist[0].address = tw_dev->generic_buffer_phys[request_id];	if (sglist[0].address & TW_ALIGNMENT_9000_SGL) {		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Found unaligned address during AEN drain");		goto out;	}	/* Mark internal command */	tw_dev->srb[request_id] = NULL;	do {		/* Send command to the board */		if (twa_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Error posting request sense");			goto out;		}		/* Now poll for completion */		if (twa_poll_response(tw_dev, request_id, 30)) {			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "No valid response while draining AEN queue");			tw_dev->posted_request_count--;			goto out;		}		tw_dev->posted_request_count--;		header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];		aen = header->status_block.error;		queue = 0;		count++;		switch (aen) {		case TW_AEN_QUEUE_EMPTY:			if (first_reset != 1)				goto out;			else				finished = 1;			break;		case TW_AEN_SOFT_RESET:			if (first_reset == 0)				first_reset = 1;			else				queue = 1;			break;		case TW_AEN_SYNC_TIME_WITH_HOST:			break;		default:			queue = 1;		}		/* Now queue an event info */		if (queue)			twa_aen_queue_event(tw_dev, header);	} while ((finished == 0) && (count < TW_MAX_AEN_DRAIN));	if (count == TW_MAX_AEN_DRAIN)		goto out;	retval = 0;out:	tw_dev->state[request_id] = TW_S_INITIAL;	return retval;} /* End twa_aen_drain_queue() *//* This function will queue an event */static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header){	u32 local_time;	struct timeval time;	TW_Event *event;	unsigned short aen;	char host[16];	tw_dev->aen_count++;	/* Fill out event info */	event = tw_dev->event_queue[tw_dev->error_index];	/* Check for clobber */	host[0] = '\0';	if (tw_dev->host) {		sprintf(host, " scsi%d:", tw_dev->host->host_no);		if (event->retrieved == TW_AEN_NOT_RETRIEVED)			tw_dev->aen_clobber = 1;	}	aen = header->status_block.error;	memset(event, 0, sizeof(TW_Event));	event->severity = TW_SEV_OUT(header->status_block.severity__reserved);	do_gettimeofday(&time);	local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));	event->time_stamp_sec = local_time;	event->aen_code = aen;	event->retrieved = TW_AEN_NOT_RETRIEVED;	event->sequence_id = tw_dev->error_sequence_id;	tw_dev->error_sequence_id++;	header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';	event->parameter_len = strlen(header->err_specific_desc);	memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len);	if (event->severity != TW_AEN_SEVERITY_DEBUG)		printk(KERN_WARNING "3w-9xxx:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n",		       host,		       twa_aen_severity_lookup(TW_SEV_OUT(header->status_block.severity__reserved)),		       TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen,		       twa_string_lookup(twa_aen_table, aen),		       header->err_specific_desc);	else

⌨️ 快捷键说明

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