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 + -
显示快捷键?