scsi_transport_spi.c

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

C
713
字号
/*  *  Parallel SCSI (SPI) transport specific attributes exported to sysfs. * *  Copyright (c) 2003 Silicon Graphics, Inc.  All rights reserved. * *  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/module.h>#include <linux/init.h>#include <linux/smp_lock.h>#include <linux/list.h>#include <linux/spinlock.h>#include <linux/mm.h>#include <linux/workqueue.h>#include <asm/scatterlist.h>#include <asm/io.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_request.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_transport_spi.h>#define SPI_PRINTK(x, l, f, a...)	printk(l "scsi(%d:%d:%d:%d): " f, (x)->host->host_no, (x)->channel, (x)->id, (x)->lun , ##a)static void transport_class_release(struct class_device *class_dev);#define SPI_NUM_ATTRS 10	/* increase this if you add attributes */#define SPI_OTHER_ATTRS 1	/* Increase this if you add "always				 * on" attributes */#define SPI_MAX_ECHO_BUFFER_SIZE	4096/* Private data accessors (keep these out of the header file) */#define spi_dv_pending(x) (((struct spi_transport_attrs *)&(x)->transport_data)->dv_pending)#define spi_dv_sem(x) (((struct spi_transport_attrs *)&(x)->transport_data)->dv_sem)struct spi_internal {	struct scsi_transport_template t;	struct spi_function_template *f;	/* The actual attributes */	struct class_device_attribute private_attrs[SPI_NUM_ATTRS];	/* The array of null terminated pointers to attributes 	 * needed by scsi_sysfs.c */	struct class_device_attribute *attrs[SPI_NUM_ATTRS + SPI_OTHER_ATTRS + 1];};#define to_spi_internal(tmpl)	container_of(tmpl, struct spi_internal, t)static const char *const ppr_to_ns[] = {	/* The PPR values 0-6 are reserved, fill them in when	 * the committee defines them */	NULL,			/* 0x00 */	NULL,			/* 0x01 */	NULL,			/* 0x02 */	NULL,			/* 0x03 */	NULL,			/* 0x04 */	NULL,			/* 0x05 */	NULL,			/* 0x06 */	"3.125",		/* 0x07 */	"6.25",			/* 0x08 */	"12.5",			/* 0x09 */	"25",			/* 0x0a */	"30.3",			/* 0x0b */	"50",			/* 0x0c */};/* The PPR values at which you calculate the period in ns by multiplying * by 4 */#define SPI_STATIC_PPR	0x0cstruct class spi_transport_class = {	.name = "spi_transport",	.release = transport_class_release,};static __init int spi_transport_init(void){	return class_register(&spi_transport_class);}static void __exit spi_transport_exit(void){	class_unregister(&spi_transport_class);}static int spi_setup_transport_attrs(struct scsi_device *sdev){	spi_period(sdev) = -1;	/* illegal value */	spi_offset(sdev) = 0;	/* async */	spi_width(sdev) = 0;	/* narrow */	spi_iu(sdev) = 0;	/* no IU */	spi_dt(sdev) = 0;	/* ST */	spi_qas(sdev) = 0;	spi_wr_flow(sdev) = 0;	spi_rd_strm(sdev) = 0;	spi_rti(sdev) = 0;	spi_pcomp_en(sdev) = 0;	spi_dv_pending(sdev) = 0;	init_MUTEX(&spi_dv_sem(sdev));	return 0;}static void transport_class_release(struct class_device *class_dev){	struct scsi_device *sdev = transport_class_to_sdev(class_dev);	put_device(&sdev->sdev_gendev);}#define spi_transport_show_function(field, format_string)		\									\static ssize_t								\show_spi_transport_##field(struct class_device *cdev, char *buf)	\{									\	struct scsi_device *sdev = transport_class_to_sdev(cdev);	\	struct spi_transport_attrs *tp;					\	struct spi_internal *i = to_spi_internal(sdev->host->transportt); \	tp = (struct spi_transport_attrs *)&sdev->transport_data;	\	if (i->f->get_##field)						\		i->f->get_##field(sdev);				\	return snprintf(buf, 20, format_string, tp->field);		\}#define spi_transport_store_function(field, format_string)		\static ssize_t								\store_spi_transport_##field(struct class_device *cdev, const char *buf, \			    size_t count)				\{									\	int val;							\	struct scsi_device *sdev = transport_class_to_sdev(cdev);	\	struct spi_internal *i = to_spi_internal(sdev->host->transportt); \									\	val = simple_strtoul(buf, NULL, 0);				\	i->f->set_##field(sdev, val);					\	return count;							\}#define spi_transport_rd_attr(field, format_string)			\	spi_transport_show_function(field, format_string)		\	spi_transport_store_function(field, format_string)		\static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR,			\			 show_spi_transport_##field,			\			 store_spi_transport_##field);/* The Parallel SCSI Tranport Attributes: */spi_transport_rd_attr(offset, "%d\n");spi_transport_rd_attr(width, "%d\n");spi_transport_rd_attr(iu, "%d\n");spi_transport_rd_attr(dt, "%d\n");spi_transport_rd_attr(qas, "%d\n");spi_transport_rd_attr(wr_flow, "%d\n");spi_transport_rd_attr(rd_strm, "%d\n");spi_transport_rd_attr(rti, "%d\n");spi_transport_rd_attr(pcomp_en, "%d\n");static ssize_tstore_spi_revalidate(struct class_device *cdev, const char *buf, size_t count){	struct scsi_device *sdev = transport_class_to_sdev(cdev);	spi_dv_device(sdev);	return count;}static CLASS_DEVICE_ATTR(revalidate, S_IWUSR, NULL, store_spi_revalidate);/* Translate the period into ns according to the current spec * for SDTR/PPR messages */static ssize_t show_spi_transport_period(struct class_device *cdev, char *buf){	struct scsi_device *sdev = transport_class_to_sdev(cdev);	struct spi_transport_attrs *tp;	const char *str;	struct spi_internal *i = to_spi_internal(sdev->host->transportt);	tp = (struct spi_transport_attrs *)&sdev->transport_data;	if (i->f->get_period)		i->f->get_period(sdev);	switch(tp->period) {	case 0x07 ... SPI_STATIC_PPR:		str = ppr_to_ns[tp->period];		if(!str)			str = "reserved";		break;	case (SPI_STATIC_PPR+1) ... 0xff:		return sprintf(buf, "%d\n", tp->period * 4);	default:		str = "unknown";	}	return sprintf(buf, "%s\n", str);}static ssize_tstore_spi_transport_period(struct class_device *cdev, const char *buf,			    size_t count){	struct scsi_device *sdev = transport_class_to_sdev(cdev);	struct spi_internal *i = to_spi_internal(sdev->host->transportt);	int j, period = -1;	for (j = 0; j < SPI_STATIC_PPR; j++) {		int len;		if(ppr_to_ns[j] == NULL)			continue;		len = strlen(ppr_to_ns[j]);		if(strncmp(ppr_to_ns[j], buf, len) != 0)			continue;		if(buf[len] != '\n')			continue;				period = j;		break;	}	if (period == -1) {		int val = simple_strtoul(buf, NULL, 0);		/* Should probably check limits here, but this		 * gets reasonably close to OK for most things */		period = val/4;	}	if (period > 0xff)		period = 0xff;	i->f->set_period(sdev, period);	return count;}	static CLASS_DEVICE_ATTR(period, S_IRUGO | S_IWUSR, 			 show_spi_transport_period,			 store_spi_transport_period);#define DV_SET(x, y)			\	if(i->f->set_##x)		\		i->f->set_##x(sdev, y)#define DV_LOOPS	3#define DV_TIMEOUT	(10*HZ)#define DV_RETRIES	3	/* should only need at most 				 * two cc/ua clears *//* This is for read/write Domain Validation:  If the device supports * an echo buffer, we do read/write tests to it */static intspi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer,			  u8 *ptr, const int retries){	struct scsi_device *sdev = sreq->sr_device;	int len = ptr - buffer;	int j, k, r;	unsigned int pattern = 0x0000ffff;	const char spi_write_buffer[] = {		WRITE_BUFFER, 0x0a, 0, 0, 0, 0, 0, len >> 8, len & 0xff, 0	};	const char spi_read_buffer[] = {		READ_BUFFER, 0x0a, 0, 0, 0, 0, 0, len >> 8, len & 0xff, 0	};	/* set up the pattern buffer.  Doesn't matter if we spill	 * slightly beyond since that's where the read buffer is */	for (j = 0; j < len; ) {		/* fill the buffer with counting (test a) */		for ( ; j < min(len, 32); j++)			buffer[j] = j;		k = j;		/* fill the buffer with alternating words of 0x0 and		 * 0xffff (test b) */		for ( ; j < min(len, k + 32); j += 2) {			u16 *word = (u16 *)&buffer[j];						*word = (j & 0x02) ? 0x0000 : 0xffff;		}		k = j;		/* fill with crosstalk (alternating 0x5555 0xaaa)                 * (test c) */		for ( ; j < min(len, k + 32); j += 2) {			u16 *word = (u16 *)&buffer[j];			*word = (j & 0x02) ? 0x5555 : 0xaaaa;		}		k = j;		/* fill with shifting bits (test d) */		for ( ; j < min(len, k + 32); j += 4) {			u32 *word = (unsigned int *)&buffer[j];			u32 roll = (pattern & 0x80000000) ? 1 : 0;						*word = pattern;			pattern = (pattern << 1) | roll;		}		/* don't bother with random data (test e) */	}	for (r = 0; r < retries; r++) {		sreq->sr_cmd_len = 0;	/* wait_req to fill in */		sreq->sr_data_direction = DMA_TO_DEVICE;		scsi_wait_req(sreq, spi_write_buffer, buffer, len,			      DV_TIMEOUT, DV_RETRIES);		if(sreq->sr_result || !scsi_device_online(sdev)) {			scsi_device_set_state(sdev, SDEV_QUIESCE);			SPI_PRINTK(sdev, KERN_ERR, "Write Buffer failure %x\n", sreq->sr_result);			return 0;		}		memset(ptr, 0, len);		sreq->sr_cmd_len = 0;	/* wait_req to fill in */		sreq->sr_data_direction = DMA_FROM_DEVICE;		scsi_wait_req(sreq, spi_read_buffer, ptr, len,			      DV_TIMEOUT, DV_RETRIES);		scsi_device_set_state(sdev, SDEV_QUIESCE);		if (memcmp(buffer, ptr, len) != 0)			return 0;	}	return 1;}/* This is for the simplest form of Domain Validation: a read test * on the inquiry data from the device */static intspi_dv_device_compare_inquiry(struct scsi_request *sreq, u8 *buffer,			      u8 *ptr, const int retries){	int r;	const int len = sreq->sr_device->inquiry_len;	struct scsi_device *sdev = sreq->sr_device;	const char spi_inquiry[] = {		INQUIRY, 0, 0, 0, len, 0	};

⌨️ 快捷键说明

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