/*
* 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 <asm/semaphore.h>
#include <scsi/scsi.h>
#include "scsi_priv.h"
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_request.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_spi.h>
#define SPI_PRINTK(x, l, f, a...) dev_printk(l, &(x)->dev, f , ##a)
#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_HOST_ATTRS 1
#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)->starget_data)->dv_pending)
#define spi_dv_sem(x) (((struct spi_transport_attrs *)&(x)->starget_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];
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 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;
}
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 < sizeof(signal_types)/sizeof(signal_types[0]); 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 < sizeof(signal_types)/sizeof(signal_types[0]); 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 device *dev)
{
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 *<