scsi_transport_spi.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 713 行 · 第 1/2 页
C
713 行
/* * Parallel SCSI (SPI) transport specific attributes exported to sysfs. * * Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved. * * 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/module.h>#include <linux/init.h>#include <linux/smp_lock.h>#include <linux/list.h>#include <linux/spinlock.h>#include <linux/mm.h>#include <linux/workqueue.h>#include <asm/scatterlist.h>#include <asm/io.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_request.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_transport_spi.h>#define SPI_PRINTK(x, l, f, a...) printk(l "scsi(%d:%d:%d:%d): " f, (x)->host->host_no, (x)->channel, (x)->id, (x)->lun , ##a)static void transport_class_release(struct class_device *class_dev);#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_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)->transport_data)->dv_pending)#define spi_dv_sem(x) (((struct spi_transport_attrs *)&(x)->transport_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];};#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 0x0cstruct class spi_transport_class = { .name = "spi_transport", .release = transport_class_release,};static __init int spi_transport_init(void){ return class_register(&spi_transport_class);}static void __exit spi_transport_exit(void){ class_unregister(&spi_transport_class);}static int spi_setup_transport_attrs(struct scsi_device *sdev){ spi_period(sdev) = -1; /* illegal value */ spi_offset(sdev) = 0; /* async */ spi_width(sdev) = 0; /* narrow */ spi_iu(sdev) = 0; /* no IU */ spi_dt(sdev) = 0; /* ST */ spi_qas(sdev) = 0; spi_wr_flow(sdev) = 0; spi_rd_strm(sdev) = 0; spi_rti(sdev) = 0; spi_pcomp_en(sdev) = 0; spi_dv_pending(sdev) = 0; init_MUTEX(&spi_dv_sem(sdev)); return 0;}static void transport_class_release(struct class_device *class_dev){ struct scsi_device *sdev = transport_class_to_sdev(class_dev); put_device(&sdev->sdev_gendev);}#define spi_transport_show_function(field, format_string) \ \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 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) \ 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);/* The Parallel SCSI Tranport Attributes: */spi_transport_rd_attr(offset, "%d\n");spi_transport_rd_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");static ssize_tstore_spi_revalidate(struct class_device *cdev, const char *buf, size_t count){ struct scsi_device *sdev = transport_class_to_sdev(cdev); spi_dv_device(sdev); 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 ssize_t show_spi_transport_period(struct class_device *cdev, char *buf){ struct scsi_device *sdev = transport_class_to_sdev(cdev); struct spi_transport_attrs *tp; const char *str; struct spi_internal *i = to_spi_internal(sdev->host->transportt); tp = (struct spi_transport_attrs *)&sdev->transport_data; if (i->f->get_period) i->f->get_period(sdev); switch(tp->period) { case 0x07 ... SPI_STATIC_PPR: str = ppr_to_ns[tp->period]; if(!str) str = "reserved"; break; case (SPI_STATIC_PPR+1) ... 0xff: return sprintf(buf, "%d\n", tp->period * 4); default: str = "unknown"; } return sprintf(buf, "%s\n", str);}static ssize_tstore_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; } if (period == -1) { int val = simple_strtoul(buf, NULL, 0); /* Should probably check limits here, but this * gets reasonably close to OK for most things */ period = val/4; } if (period > 0xff) period = 0xff; 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);#define DV_SET(x, y) \ if(i->f->set_##x) \ i->f->set_##x(sdev, y)#define DV_LOOPS 3#define DV_TIMEOUT (10*HZ)#define DV_RETRIES 3 /* should only need at most * two cc/ua clears *//* This is for read/write Domain Validation: If the device supports * an echo buffer, we do read/write tests to it */static intspi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer, u8 *ptr, const int retries){ struct scsi_device *sdev = sreq->sr_device; int len = ptr - buffer; int j, k, r; unsigned int pattern = 0x0000ffff; 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++) { sreq->sr_cmd_len = 0; /* wait_req to fill in */ sreq->sr_data_direction = DMA_TO_DEVICE; scsi_wait_req(sreq, spi_write_buffer, buffer, len, DV_TIMEOUT, DV_RETRIES); if(sreq->sr_result || !scsi_device_online(sdev)) { scsi_device_set_state(sdev, SDEV_QUIESCE); SPI_PRINTK(sdev, KERN_ERR, "Write Buffer failure %x\n", sreq->sr_result); return 0; } memset(ptr, 0, len); sreq->sr_cmd_len = 0; /* wait_req to fill in */ sreq->sr_data_direction = DMA_FROM_DEVICE; scsi_wait_req(sreq, spi_read_buffer, ptr, len, DV_TIMEOUT, DV_RETRIES); scsi_device_set_state(sdev, SDEV_QUIESCE); if (memcmp(buffer, ptr, len) != 0) return 0; } return 1;}/* This is for the simplest form of Domain Validation: a read test * on the inquiry data from the device */static intspi_dv_device_compare_inquiry(struct scsi_request *sreq, u8 *buffer, u8 *ptr, const int retries){ int r; const int len = sreq->sr_device->inquiry_len; struct scsi_device *sdev = sreq->sr_device; const char spi_inquiry[] = { INQUIRY, 0, 0, 0, len, 0 };
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?