Commit 1b98ec6b authored by James Bottomley's avatar James Bottomley Committed by James Bottomley

SCSI: Make SPI transport attributes mutable

This adds the final missing piece to the transport attributes:  A
published API by which they can be set and retrieved (SPI attributes
only).  The sysfs field only appears writeable if the driver supplied a
set method in the attribute template, so unsettable attributes show up
as read only.

The consequence now is that the spi transport attribute class is no
longer a simple exported structure: the driver has to attach to the spi
transport class at module initialisation.
parent f8ca1b01
...@@ -26,6 +26,41 @@ ...@@ -26,6 +26,41 @@
static void transport_class_release(struct class_device *class_dev); static void transport_class_release(struct class_device *class_dev);
#define SPI_NUM_ATTRS 10 /* increase this if you add attributes */
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 + 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 0x0c
struct class spi_transport_class = { struct class spi_transport_class = {
.name = "spi_transport", .name = "spi_transport",
.release = transport_class_release, .release = transport_class_release,
...@@ -63,19 +98,40 @@ static void transport_class_release(struct class_device *class_dev) ...@@ -63,19 +98,40 @@ static void transport_class_release(struct class_device *class_dev)
put_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev);
} }
#define spi_transport_show_function(field, format_string) \ #define spi_transport_show_function(field, format_string) \
static ssize_t \ \
show_spi_transport_##field (struct class_device *cdev, char *buf) \ 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 scsi_device *sdev = transport_class_to_sdev(cdev); \
tp = (struct spi_transport_attrs *)&sdev->transport_data; \ struct spi_transport_attrs *tp; \
return snprintf(buf, 20, format_string, tp->field); \ 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) \ #define spi_transport_rd_attr(field, format_string) \
spi_transport_show_function(field, format_string) \ spi_transport_show_function(field, format_string) \
static CLASS_DEVICE_ATTR( field, S_IRUGO, show_spi_transport_##field, NULL) 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: */ /* The Parallel SCSI Tranport Attributes: */
spi_transport_rd_attr(offset, "%d\n"); spi_transport_rd_attr(offset, "%d\n");
...@@ -95,72 +151,148 @@ static ssize_t show_spi_transport_period(struct class_device *cdev, char *buf) ...@@ -95,72 +151,148 @@ static ssize_t show_spi_transport_period(struct class_device *cdev, char *buf)
{ {
struct scsi_device *sdev = transport_class_to_sdev(cdev); struct scsi_device *sdev = transport_class_to_sdev(cdev);
struct spi_transport_attrs *tp; struct spi_transport_attrs *tp;
char *str; const char *str;
struct spi_internal *i = to_spi_internal(sdev->host->transportt);
tp = (struct spi_transport_attrs *)&sdev->transport_data; tp = (struct spi_transport_attrs *)&sdev->transport_data;
if(i->f->get_period)
i->f->get_period(sdev);
switch(tp->period) { switch(tp->period) {
case 0x00 ... 0x06:
str = "reserved";
break;
case 0x07: case 0x07 ... SPI_STATIC_PPR:
str = "3.125"; str = ppr_to_ns[tp->period];
if(!str)
str = "reserved";
break; break;
case 0x08:
str = "6.25";
break;
case 0x09: case (SPI_STATIC_PPR+1) ... 0xff:
str = "12.5"; return sprintf(buf, "%d\n", tp->period * 4);
break;
case 0x0a: default:
str = "25"; str = "unknown";
break; }
return sprintf(buf, "%s\n", str);
}
case 0x0b: static ssize_t
str = "30.3"; store_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; break;
}
case 0x0c: if(period == -1) {
str = "50"; int val = simple_strtoul(buf, NULL, 0);
case 0x0d ... 0xff:
return sprintf(buf, "%d\n", tp->period * 4); if(val >= (SPI_STATIC_PPR + 1)*4)
period = val/4;
default:
str = "unknown";
} }
return sprintf(buf, "%s\n", str);
if(period == -1 || period > 0xff)
return -EINVAL;
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);
static CLASS_DEVICE_ATTR(period, S_IRUGO, show_spi_transport_period, NULL);
struct class_device_attribute *spi_transport_attrs[] = {
&class_device_attr_period,
&class_device_attr_offset,
&class_device_attr_width,
&class_device_attr_iu,
&class_device_attr_dt,
&class_device_attr_qas,
&class_device_attr_wr_flow,
&class_device_attr_rd_strm,
&class_device_attr_rti,
&class_device_attr_pcomp_en,
NULL
};
struct scsi_transport_template spi_transport_template = { struct scsi_transport_template spi_transport_template = {
.attrs = spi_transport_attrs,
.class = &spi_transport_class, .class = &spi_transport_class,
.setup = &spi_setup_transport_attrs, .setup = &spi_setup_transport_attrs,
.cleanup = NULL, .cleanup = NULL,
.size = sizeof(struct spi_transport_attrs) - sizeof(unsigned long), .size = sizeof(struct spi_transport_attrs) - sizeof(unsigned long),
}; };
EXPORT_SYMBOL(spi_transport_template);
#define SETUP_ATTRIBUTE(field) \
i->private_attrs[count] = class_device_attr_##field; \
if(!i->f->set_##field) { \
i->private_attrs[count].attr.mode = S_IRUGO; \
i->private_attrs[count].store = NULL; \
} \
i->attrs[count] = &i->private_attrs[count]; \
count++
struct scsi_transport_template *
spi_attach_transport(struct spi_function_template *ft)
{
struct spi_internal *i = kmalloc(sizeof(struct spi_internal),
GFP_KERNEL);
int count = 0;
if(!i)
return NULL;
memset(i, 0, sizeof(struct spi_internal));
i->t.attrs = &i->attrs[0];
i->t.class = &spi_transport_class;
i->t.setup = &spi_setup_transport_attrs;
i->t.size = sizeof(struct spi_transport_attrs) - sizeof(unsigned long);
i->f = ft;
SETUP_ATTRIBUTE(period);
SETUP_ATTRIBUTE(offset);
SETUP_ATTRIBUTE(width);
SETUP_ATTRIBUTE(iu);
SETUP_ATTRIBUTE(dt);
SETUP_ATTRIBUTE(qas);
SETUP_ATTRIBUTE(wr_flow);
SETUP_ATTRIBUTE(rd_strm);
SETUP_ATTRIBUTE(rti);
SETUP_ATTRIBUTE(pcomp_en);
/* if you add an attribute but forget to increase SPI_NUM_ATTRS
* this bug will trigger */
BUG_ON(count != SPI_NUM_ATTRS);
i->attrs[count] = NULL;
return &i->t;
}
EXPORT_SYMBOL(spi_attach_transport);
void spi_release_transport(struct scsi_transport_template *t)
{
struct spi_internal *i = to_spi_internal(t);
kfree(i);
}
EXPORT_SYMBOL(spi_release_transport);
MODULE_AUTHOR("Martin Hicks"); MODULE_AUTHOR("Martin Hicks");
MODULE_DESCRIPTION("SPI Transport Attributes"); MODULE_DESCRIPTION("SPI Transport Attributes");
......
...@@ -48,6 +48,32 @@ struct spi_transport_attrs { ...@@ -48,6 +48,32 @@ struct spi_transport_attrs {
#define spi_rd_strm(x) (((struct spi_transport_attrs *)&(x)->transport_data)->rd_strm) #define spi_rd_strm(x) (((struct spi_transport_attrs *)&(x)->transport_data)->rd_strm)
#define spi_rti(x) (((struct spi_transport_attrs *)&(x)->transport_data)->rti) #define spi_rti(x) (((struct spi_transport_attrs *)&(x)->transport_data)->rti)
#define spi_pcomp_en(x) (((struct spi_transport_attrs *)&(x)->transport_data)->pcomp_en) #define spi_pcomp_en(x) (((struct spi_transport_attrs *)&(x)->transport_data)->pcomp_en)
extern struct scsi_transport_template spi_transport_template;
/* The functions by which the transport class and the driver communicate */
struct spi_function_template {
void (*get_period)(struct scsi_device *);
void (*set_period)(struct scsi_device *, int);
void (*get_offset)(struct scsi_device *);
void (*set_offset)(struct scsi_device *, int);
void (*get_width)(struct scsi_device *);
void (*set_width)(struct scsi_device *, int);
void (*get_iu)(struct scsi_device *);
void (*set_iu)(struct scsi_device *, int);
void (*get_dt)(struct scsi_device *);
void (*set_dt)(struct scsi_device *, int);
void (*get_qas)(struct scsi_device *);
void (*set_qas)(struct scsi_device *, int);
void (*get_wr_flow)(struct scsi_device *);
void (*set_wr_flow)(struct scsi_device *, int);
void (*get_rd_strm)(struct scsi_device *);
void (*set_rd_strm)(struct scsi_device *, int);
void (*get_rti)(struct scsi_device *);
void (*set_rti)(struct scsi_device *, int);
void (*get_pcomp_en)(struct scsi_device *);
void (*set_pcomp_en)(struct scsi_device *, int);
};
struct scsi_transport_template *spi_attach_transport(struct spi_function_template *);
void spi_release_transport(struct scsi_transport_template *);
#endif /* SCSI_TRANSPORT_SPI_H */ #endif /* SCSI_TRANSPORT_SPI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment