scsi_transport_spi.c

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

C
1,441
字号
/*  *  Parallel SCSI (SPI) transport specific attributes exported to sysfs. * *  Copyright (c) 2003 Silicon Graphics, Inc.  All rights reserved. *  Copyright (c) 2004, 2005 James Bottomley <James.Bottomley@SteelEye.com> * *  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/module.h>#include <linux/workqueue.h>#include <linux/blkdev.h>#include <linux/mutex.h>#include <scsi/scsi.h>#include "scsi_priv.h"#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_eh.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_transport_spi.h>#define SPI_NUM_ATTRS 14	/* increase this if you add attributes */#define SPI_OTHER_ATTRS 1	/* Increase this if you add "always				 * on" attributes */#define SPI_HOST_ATTRS	1#define SPI_MAX_ECHO_BUFFER_SIZE	4096#define DV_LOOPS	3#define DV_TIMEOUT	(10*HZ)#define DV_RETRIES	3	/* should only need at most 				 * two cc/ua clears *//* Private data accessors (keep these out of the header file) */#define spi_dv_in_progress(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_in_progress)#define spi_dv_mutex(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_mutex)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];	struct class_device_attribute private_host_attrs[SPI_HOST_ATTRS];	struct class_device_attribute *host_attrs[SPI_HOST_ATTRS + 1];};#define to_spi_internal(tmpl)	container_of(tmpl, struct spi_internal, t)static const int ppr_to_ps[] = {	/* The PPR values 0-6 are reserved, fill them in when	 * the committee defines them */	-1,			/* 0x00 */	-1,			/* 0x01 */	-1,			/* 0x02 */	-1,			/* 0x03 */	-1,			/* 0x04 */	-1,			/* 0x05 */	-1,			/* 0x06 */	 3125,			/* 0x07 */	 6250,			/* 0x08 */	12500,			/* 0x09 */	25000,			/* 0x0a */	30300,			/* 0x0b */	50000,			/* 0x0c */};/* The PPR values at which you calculate the period in ns by multiplying * by 4 */#define SPI_STATIC_PPR	0x0cstatic int sprint_frac(char *dest, int value, int denom){	int frac = value % denom;	int result = sprintf(dest, "%d", value / denom);	if (frac == 0)		return result;	dest[result++] = '.';	do {		denom /= 10;		sprintf(dest + result, "%d", frac / denom);		result++;		frac %= denom;	} while (frac);	dest[result++] = '\0';	return result;}static int spi_execute(struct scsi_device *sdev, const void *cmd,		       enum dma_data_direction dir,		       void *buffer, unsigned bufflen,		       struct scsi_sense_hdr *sshdr){	int i, result;	unsigned char sense[SCSI_SENSE_BUFFERSIZE];	for(i = 0; i < DV_RETRIES; i++) {		result = scsi_execute(sdev, cmd, dir, buffer, bufflen,				      sense, DV_TIMEOUT, /* retries */ 1,				      REQ_FAILFAST);		if (result & DRIVER_SENSE) {			struct scsi_sense_hdr sshdr_tmp;			if (!sshdr)				sshdr = &sshdr_tmp;			if (scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE,						 sshdr)			    && sshdr->sense_key == UNIT_ATTENTION)				continue;		}		break;	}	return result;}static struct {	enum spi_signal_type	value;	char			*name;} signal_types[] = {	{ SPI_SIGNAL_UNKNOWN, "unknown" },	{ SPI_SIGNAL_SE, "SE" },	{ SPI_SIGNAL_LVD, "LVD" },	{ SPI_SIGNAL_HVD, "HVD" },};static inline const char *spi_signal_to_string(enum spi_signal_type type){	int i;	for (i = 0; i < ARRAY_SIZE(signal_types); i++) {		if (type == signal_types[i].value)			return signal_types[i].name;	}	return NULL;}static inline enum spi_signal_type spi_signal_to_value(const char *name){	int i, len;	for (i = 0; i < ARRAY_SIZE(signal_types); i++) {		len =  strlen(signal_types[i].name);		if (strncmp(name, signal_types[i].name, len) == 0 &&		    (name[len] == '\n' || name[len] == '\0'))			return signal_types[i].value;	}	return SPI_SIGNAL_UNKNOWN;}static int spi_host_setup(struct transport_container *tc, struct device *dev,			  struct class_device *cdev){	struct Scsi_Host *shost = dev_to_shost(dev);	spi_signalling(shost) = SPI_SIGNAL_UNKNOWN;	return 0;}static DECLARE_TRANSPORT_CLASS(spi_host_class,			       "spi_host",			       spi_host_setup,			       NULL,			       NULL);static int spi_host_match(struct attribute_container *cont,			  struct device *dev){	struct Scsi_Host *shost;	struct spi_internal *i;	if (!scsi_is_host_device(dev))		return 0;	shost = dev_to_shost(dev);	if (!shost->transportt  || shost->transportt->host_attrs.ac.class	    != &spi_host_class.class)		return 0;	i = to_spi_internal(shost->transportt);		return &i->t.host_attrs.ac == cont;}static int spi_device_configure(struct transport_container *tc,				struct device *dev,				struct class_device *cdev){	struct scsi_device *sdev = to_scsi_device(dev);	struct scsi_target *starget = sdev->sdev_target;	/* Populate the target capability fields with the values	 * gleaned from the device inquiry */	spi_support_sync(starget) = scsi_device_sync(sdev);	spi_support_wide(starget) = scsi_device_wide(sdev);	spi_support_dt(starget) = scsi_device_dt(sdev);	spi_support_dt_only(starget) = scsi_device_dt_only(sdev);	spi_support_ius(starget) = scsi_device_ius(sdev);	spi_support_qas(starget) = scsi_device_qas(sdev);	return 0;}static int spi_setup_transport_attrs(struct transport_container *tc,				     struct device *dev,				     struct class_device *cdev){	struct scsi_target *starget = to_scsi_target(dev);	spi_period(starget) = -1;	/* illegal value */	spi_min_period(starget) = 0;	spi_offset(starget) = 0;	/* async */	spi_max_offset(starget) = 255;	spi_width(starget) = 0;	/* narrow */	spi_max_width(starget) = 1;	spi_iu(starget) = 0;	/* no IU */	spi_dt(starget) = 0;	/* ST */	spi_qas(starget) = 0;	spi_wr_flow(starget) = 0;	spi_rd_strm(starget) = 0;	spi_rti(starget) = 0;	spi_pcomp_en(starget) = 0;	spi_hold_mcs(starget) = 0;	spi_dv_pending(starget) = 0;	spi_dv_in_progress(starget) = 0;	spi_initial_dv(starget) = 0;	mutex_init(&spi_dv_mutex(starget));	return 0;}#define spi_transport_show_simple(field, format_string)			\									\static ssize_t								\show_spi_transport_##field(struct class_device *cdev, char *buf)	\{									\	struct scsi_target *starget = transport_class_to_starget(cdev);	\	struct spi_transport_attrs *tp;					\									\	tp = (struct spi_transport_attrs *)&starget->starget_data;	\	return snprintf(buf, 20, format_string, tp->field);		\}#define spi_transport_store_simple(field, format_string)		\									\static ssize_t								\store_spi_transport_##field(struct class_device *cdev, const char *buf, \			    size_t count)				\{									\	int val;							\	struct scsi_target *starget = transport_class_to_starget(cdev);	\	struct spi_transport_attrs *tp;					\									\	tp = (struct spi_transport_attrs *)&starget->starget_data;	\	val = simple_strtoul(buf, NULL, 0);				\	tp->field = val;						\	return count;							\}#define spi_transport_show_function(field, format_string)		\									\static ssize_t								\show_spi_transport_##field(struct class_device *cdev, char *buf)	\{									\	struct scsi_target *starget = transport_class_to_starget(cdev);	\	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);	\	struct spi_transport_attrs *tp;					\	struct spi_internal *i = to_spi_internal(shost->transportt);	\	tp = (struct spi_transport_attrs *)&starget->starget_data;	\	if (i->f->get_##field)						\		i->f->get_##field(starget);				\	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_target *starget = transport_class_to_starget(cdev);	\	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);	\	struct spi_internal *i = to_spi_internal(shost->transportt);	\									\	val = simple_strtoul(buf, NULL, 0);				\	i->f->set_##field(starget, val);			\	return count;							\}#define spi_transport_store_max(field, format_string)			\static ssize_t								\store_spi_transport_##field(struct class_device *cdev, const char *buf, \			    size_t count)				\{									\	int val;							\	struct scsi_target *starget = transport_class_to_starget(cdev);	\	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);	\	struct spi_internal *i = to_spi_internal(shost->transportt);	\	struct spi_transport_attrs *tp					\		= (struct spi_transport_attrs *)&starget->starget_data;	\									\	val = simple_strtoul(buf, NULL, 0);				\	if (val > tp->max_##field)					\		val = tp->max_##field;					\	i->f->set_##field(starget, 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);#define spi_transport_simple_attr(field, format_string)			\	spi_transport_show_simple(field, format_string)			\	spi_transport_store_simple(field, format_string)		\static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR,			\			 show_spi_transport_##field,			\			 store_spi_transport_##field);#define spi_transport_max_attr(field, format_string)			\	spi_transport_show_function(field, format_string)		\	spi_transport_store_max(field, format_string)			\	spi_transport_simple_attr(max_##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_max_attr(offset, "%d\n");spi_transport_max_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");spi_transport_rd_attr(hold_mcs, "%d\n");/* we only care about the first child device so we return 1 */static int child_iter(struct device *dev, void *data){	struct scsi_device *sdev = to_scsi_device(dev);	spi_dv_device(sdev);	return 1;}static ssize_tstore_spi_revalidate(struct class_device *cdev, const char *buf, size_t count){	struct scsi_target *starget = transport_class_to_starget(cdev);	device_for_each_child(&starget->dev, NULL, child_iter);	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 int period_to_str(char *buf, int period){	int len, picosec;	if (period < 0 || period > 0xff) {		picosec = -1;	} else if (period <= SPI_STATIC_PPR) {		picosec = ppr_to_ps[period];	} else {		picosec = period * 4000;	}	if (picosec == -1) {		len = sprintf(buf, "reserved");	} else {		len = sprint_frac(buf, picosec, 1000);	}	return len;}static ssize_tshow_spi_transport_period_helper(char *buf, int period){	int len = period_to_str(buf, period);	buf[len++] = '\n';	buf[len] = '\0';	return len;}static ssize_tstore_spi_transport_period_helper(struct class_device *cdev, const char *buf,				  size_t count, int *periodp){	int j, picosec, period = -1;	char *endp;	picosec = simple_strtoul(buf, &endp, 10) * 1000;	if (*endp == '.') {		int mult = 100;		do {			endp++;			if (!isdigit(*endp))				break;			picosec += (*endp - '0') * mult;			mult /= 10;		} while (mult > 0);	}	for (j = 0; j <= SPI_STATIC_PPR; j++) {		if (ppr_to_ps[j] < picosec)			continue;		period = j;		break;	}	if (period == -1)		period = picosec / 4000;	if (period > 0xff)		period = 0xff;	*periodp = period;	return count;}static ssize_tshow_spi_transport_period(struct class_device *cdev, char *buf){	struct scsi_target *starget = transport_class_to_starget(cdev);	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);	struct spi_internal *i = to_spi_internal(shost->transportt);	struct spi_transport_attrs *tp =		(struct spi_transport_attrs *)&starget->starget_data;	if (i->f->get_period)		i->f->get_period(starget);	return show_spi_transport_period_helper(buf, tp->period);}static ssize_tstore_spi_transport_period(struct class_device *cdev, const char *buf,			    size_t count){	struct scsi_target *starget = transport_class_to_starget(cdev);	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);	struct spi_internal *i = to_spi_internal(shost->transportt);	struct spi_transport_attrs *tp =		(struct spi_transport_attrs *)&starget->starget_data;	int period, retval;	retval = store_spi_transport_period_helper(cdev, buf, count, &period);	if (period < tp->min_period)		period = tp->min_period;	i->f->set_period(starget, period);

⌨️ 快捷键说明

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