Release 4.11 drivers/scsi/scsi_transport_spi.c
/*
* 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 <linux/sysfs.h>
#include <linux/slab.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_tcq.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 */
/* Our blacklist flags */
enum {
SPI_BLIST_NOIUS = 0x1,
};
/* blacklist table, modelled on scsi_devinfo.c */
static struct {
char *vendor;
char *model;
unsigned flags;
}
spi_static_device_list[] __initdata = {
{"HP", "Ultrium 3-SCSI", SPI_BLIST_NOIUS },
{"IBM", "ULTRIUM-TD3", SPI_BLIST_NOIUS },
{NULL, NULL, 0}
};
/* 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;
};
#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 0x0c
static 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Matthew Wilcox | 97 | 100.00% | 1 | 100.00% |
Total | 97 | 100.00% | 1 | 100.00% |
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];
struct scsi_sense_hdr sshdr_tmp;
if (!sshdr)
sshdr = &sshdr_tmp;
for(i = 0; i < DV_RETRIES; i++) {
result = scsi_execute(sdev, cmd, dir, buffer, bufflen, sense,
sshdr, DV_TIMEOUT, /* retries */ 1,
REQ_FAILFAST_DEV |
REQ_FAILFAST_TRANSPORT |
REQ_FAILFAST_DRIVER,
0, NULL);
if (!(driver_byte(result) & DRIVER_SENSE) ||
sshdr->sense_key != UNIT_ATTENTION)
break;
}
return result;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 96 | 74.42% | 2 | 33.33% |
Christoph Hellwig | 23 | 17.83% | 1 | 16.67% |
FUJITA Tomonori | 5 | 3.88% | 2 | 33.33% |
Mike Christie | 5 | 3.88% | 1 | 16.67% |
Total | 129 | 100.00% | 6 | 100.00% |
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 55 | 98.21% | 1 | 50.00% |
Tobias Klauser | 1 | 1.79% | 1 | 50.00% |
Total | 56 | 100.00% | 2 | 100.00% |
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 92 | 98.92% | 1 | 50.00% |
Tobias Klauser | 1 | 1.08% | 1 | 50.00% |
Total | 93 | 100.00% | 2 | 100.00% |
static int spi_host_setup(struct transport_container *tc, struct device *dev,
struct device *cdev)
{
struct Scsi_Host *shost = dev_to_shost(dev);
spi_signalling(shost) = SPI_SIGNAL_UNKNOWN;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 40 | 97.56% | 4 | 80.00% |
Tony Jones | 1 | 2.44% | 1 | 20.00% |
Total | 41 | 100.00% | 5 | 100.00% |
static int spi_host_configure(struct transport_container *tc,
struct device *dev,
struct device *cdev);
static DECLARE_TRANSPORT_CLASS(spi_host_class,
"spi_host",
spi_host_setup,
NULL,
spi_host_configure);
static int spi_host_match(struct attribute_container *cont,
struct device *dev)
{
struct Scsi_Host *shost;
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;
return &shost->transportt->host_attrs.ac == cont;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 76 | 100.00% | 6 | 100.00% |
Total | 76 | 100.00% | 6 | 100.00% |
static int spi_target_configure(struct transport_container *tc,
struct device *dev,
struct device *cdev);
static int spi_device_configure(struct transport_container *tc,
struct device *dev,
struct device *cdev)
{
struct scsi_device *sdev = to_scsi_device(dev);
struct scsi_target *starget = sdev->sdev_target;
unsigned bflags = scsi_get_device_flags_keyed(sdev, &sdev->inquiry[8],
&sdev->inquiry[16],
SCSI_DEVINFO_SPI);
/* 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);
if (bflags & SPI_BLIST_NOIUS) {
dev_info(dev, "Information Units disabled by blacklist\n");
spi_support_ius(starget) = 0;
}
spi_support_qas(starget) = scsi_device_qas(sdev);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 151 | 99.34% | 4 | 80.00% |
Tony Jones | 1 | 0.66% | 1 | 20.00% |
Total | 152 | 100.00% | 5 | 100.00% |
static int spi_setup_transport_attrs(struct transport_container *tc,
struct device *dev,
struct 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_max_iu(starget) = 1;
spi_dt(starget) = 0; /* ST */
spi_qas(starget) = 0;
spi_max_qas(starget) = 1;
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 179 | 98.35% | 11 | 84.62% |
Jes Sorensen | 2 | 1.10% | 1 | 7.69% |
Tony Jones | 1 | 0.55% | 1 | 7.69% |
Total | 182 | 100.00% | 13 | 100.00% |
#define spi_transport_show_simple(field, format_string) \
\
static ssize_t \
show_spi_transport_##field(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct scsi_target *starget = transport_class_to_starget(dev); \
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 device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
int val; \
struct scsi_target *starget = transport_class_to_starget(dev); \
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 device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct scsi_target *starget = transport_class_to_starget(dev); \
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 device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
int val; \
struct scsi_target *starget = transport_class_to_starget(dev); \
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
struct spi_internal *i = to_spi_internal(shost->transportt); \
\
if (!i->f->set_##field) \
return -EINVAL; \
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 device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
int val; \
struct scsi_target *starget = transport_class_to_starget(dev); \
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->set_##field) \
return -EINVAL; \
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 DEVICE_ATTR(field, S_IRUGO, \
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 DEVICE_ATTR(field, S_IRUGO, \
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 DEVICE_ATTR(field, S_IRUGO, \
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_max_attr(iu, "%d\n");
spi_transport_rd_attr(dt, "%d\n");
spi_transport_max_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 that's a real SCSI device
* so we return 1 to terminate the iteration when we find it */
static int child_iter(struct device *dev, void *data)
{
if (!scsi_is_sdev_device(dev))
return 0;
spi_dv_device(to_scsi_device(dev));
return 1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Greg Kroah-Hartman | 25 | 67.57% | 1 | 50.00% |
James Bottomley | 12 | 32.43% | 1 | 50.00% |
Total | 37 | 100.00% | 2 | 100.00% |
static ssize_t
store_spi_revalidate(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct scsi_target *starget = transport_class_to_starget(dev);
device_for_each_child(&starget->dev, NULL, child_iter);
return count;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 37 | 75.51% | 3 | 60.00% |
Tony Jones | 8 | 16.33% | 1 | 20.00% |
Greg Kroah-Hartman | 4 | 8.16% | 1 | 20.00% |
Total | 49 | 100.00% | 5 | 100.00% |
static 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Matthew Wilcox | 55 | 57.89% | 3 | 37.50% |
James Bottomley | 33 | 34.74% | 4 | 50.00% |
Andrew Morton | 7 | 7.37% | 1 | 12.50% |
Total | 95 | 100.00% | 8 | 100.00% |
static ssize_t
show_spi_transport_period_helper(char *buf, int period)
{
int len = period_to_str(buf, period);
buf[len++] = '\n';
buf[len] = '\0';
return len;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Matthew Wilcox | 22 | 53.66% | 1 | 50.00% |
James Bottomley | 19 | 46.34% | 1 | 50.00% |
Total | 41 | 100.00% | 2 | 100.00% |
static ssize_t
store_spi_transport_period_helper(struct device *dev, 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 162 | 98.78% | 1 | 50.00% |
Tony Jones | 2 | 1.22% | 1 | 50.00% |
Total | 164 | 100.00% | 2 | 100.00% |
static ssize_t
show_spi_transport_period(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scsi_target *starget = transport_class_to_starget(dev);
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 90 | 91.84% | 1 | 50.00% |
Tony Jones | 8 | 8.16% | 1 | 50.00% |
Total | 98 | 100.00% | 2 | 100.00% |
static ssize_t
store_spi_transport_period(struct device *cdev, struct device_attribute *attr,
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;
if (!i->f->set_period)
return -EINVAL;
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);
return retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 129 | 95.56% | 2 | 66.67% |
Tony Jones | 6 | 4.44% | 1 | 33.33% |
Total | 135 | 100.00% | 3 | 100.00% |
static DEVICE_ATTR(period, S_IRUGO,
show_spi_transport_period,
store_spi_transport_period);
static ssize_t
show_spi_transport_min_period(struct device *cdev,
struct device_attribute *attr, 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->set_period)
return -EINVAL;
return show_spi_transport_period_helper(buf, tp->min_period);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 87 | 92.55% | 3 | 60.00% |
Tony Jones | 6 | 6.38% | 1 | 20.00% |
Andrew Morton | 1 | 1.06% | 1 | 20.00% |
Total | 94 | 100.00% | 5 | 100.00% |
static ssize_t
store_spi_transport_min_period(struct device *cdev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct scsi_target *starget = transport_class_to_starget(cdev);
struct spi_transport_attrs *tp =
(struct spi_transport_attrs *)&starget->starget_data;
return store_spi_transport_period_helper(cdev, buf, count,
&tp->min_period);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 52 | 81.25% | 3 | 50.00% |
Tony Jones | 6 | 9.38% | 1 | 16.67% |
Matthew Wilcox | 5 | 7.81% | 1 | 16.67% |
Andrew Morton | 1 | 1.56% | 1 | 16.67% |
Total | 64 | 100.00% | 6 | 100.00% |
static DEVICE_ATTR(min_period, S_IRUGO,
show_spi_transport_min_period,
store_spi_transport_min_period);
static ssize_t show_spi_host_signalling(struct device *cdev,
struct device_attribute *attr,
char *buf)
{
struct Scsi_Host *shost = transport_class_to_shost(cdev);
struct spi_internal *i = to_spi_internal(shost->transportt);
if (i->f->get_signalling)
i->f->get_signalling(shost);
return sprintf(buf, "%s\n", spi_signal_to_string(spi_signalling(shost)));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 69 | 92.00% | 1 | 50.00% |
Tony Jones | 6 | 8.00% | 1 | 50.00% |
Total | 75 | 100.00% | 2 | 100.00% |
static ssize_t store_spi_host_signalling(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct Scsi_Host *shost = transport_class_to_shost(dev);
struct spi_internal *i = to_spi_internal(shost->transportt);
enum spi_signal_type type = spi_signal_to_value(buf);
if (!i->f->set_signalling)
return -EINVAL;
if (type != SPI_SIGNAL_UNKNOWN)
i->f->set_signalling(shost, type);
return count;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
James Bottomley | 80 | 90.91% | 2 | 66.67% |
Tony Jones | 8 | 9.09% | 1 | 33.33% |
Total | 88 | 100.00% | 3 | 100.00% |
static DEVICE_ATTR(signalling, S_IRUGO,
show_spi_host_signalling,
store_spi_host_signalling);
static ssize_t show_spi_host_width(struct device *cdev,
struct device_attribute *attr,
char *buf)
{
struct Scsi_Host *shost = transport_class_to_shost(cdev);
return sprintf(buf, "%s\n", shost->max_id == 16 ? "wide" : "narrow");
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Hannes Reinecke | 48 | 100.00% | 1 | 100.00% |
Total | 48 | 100.00% | 1 | 100.00% |
static DEVICE_ATTR(host_width, S_IRUGO,
show_spi_host_width, NULL);
static ssize_t show_spi_host_hba_id(struct device *cdev,
struct device_attribute *attr,
char *buf)
{
struct Scsi_Host *shost = transport_class_to_shost(cdev);
return sprintf(buf, "%d\n", shost->this_id);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Hannes Reinecke | 42 | 100.00% | 1 | 100.00% |
Total | 42 | 100.00% | 1 | 100.00% |
static DEVICE_ATTR(hba_id, S_IRUGO,
show_spi_host_hba_id, NULL);
#define DV_SET(x, y) \
if(i->f->set_##x) \
i->f->set_##x(sdev->sdev_target, y)
enum spi_compare_returns {
SPI_COMPARE_SUCCESS,
SPI_COMPARE_FAILURE,
SPI_COMPARE_SKIP_TEST,
};
/* This is for read/write Domain Validation: If the device supports
* an echo buffer, we do read/write tests to it */
static enum spi_compare_returns
spi_dv_device_echo_buffer(struct scsi_device *sdev, u8 *buffer,
u8 *ptr, const int retries)
{
int len = ptr - buffer;
int j, k, r, result;
unsigned int pattern = 0x0000ffff;
struct scsi_sense_hdr sshdr;
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++) {
result = spi_execute(sdev, spi_write_buffer, DMA_TO_DEVICE,
buffer, len, &sshdr);
if(result || !scsi_device_online(sdev)) {
scsi_device_set_state(sdev, SDEV_QUIESCE);
if (scsi_sense_valid(&sshdr)
&& sshdr.sense_key == ILLEGAL_REQUEST
/* INVALID FIELD IN CDB */
&& sshdr.asc == 0x24 && sshdr.ascq == 0x00)
/* This would mean that the drive lied
* to us about supporting an echo
* buffer (unfortunately some Western
* Digital drives do precisely this)
*/
return SPI_COMPARE_SKIP_TEST;
sdev_printk(KERN_ERR, sdev, "Write Buffer failure %x\n", result);
return SPI_COMPARE_FAILURE;
}
memset(ptr, 0, len);
spi_execute(sdev, spi_read_buffer, DMA_FROM_DEVICE