scsi_transport_spi.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 713 行 · 第 1/2 页
C
713 行
for (r = 0; r < retries; r++) { sreq->sr_cmd_len = 0; /* wait_req to fill in */ sreq->sr_data_direction = DMA_FROM_DEVICE; memset(ptr, 0, len); scsi_wait_req(sreq, spi_inquiry, ptr, len, DV_TIMEOUT, DV_RETRIES); if(sreq->sr_result || !scsi_device_online(sdev)) { scsi_device_set_state(sdev, SDEV_QUIESCE); return 0; } /* If we don't have the inquiry data already, the * first read gets it */ if (ptr == buffer) { ptr += len; --r; continue; } if (memcmp(buffer, ptr, len) != 0) /* failure */ return 0; } return 1;}static intspi_dv_retrain(struct scsi_request *sreq, u8 *buffer, u8 *ptr, int (*compare_fn)(struct scsi_request *, u8 *, u8 *, int)){ struct spi_internal *i = to_spi_internal(sreq->sr_host->transportt); struct scsi_device *sdev = sreq->sr_device; int period = 0, prevperiod = 0; for (;;) { int newperiod; if (compare_fn(sreq, buffer, ptr, DV_LOOPS)) /* Successful DV */ break; /* OK, retrain, fallback */ if (i->f->get_period) i->f->get_period(sdev); newperiod = spi_period(sdev); period = newperiod > period ? newperiod : period; if (period < 0x0d) period++; else period += period >> 1; if (unlikely(period > 0xff || period == prevperiod)) { /* Total failure; set to async and return */ SPI_PRINTK(sdev, KERN_ERR, "Domain Validation Failure, dropping back to Asynchronous\n"); DV_SET(offset, 0); return 0; } SPI_PRINTK(sdev, KERN_ERR, "Domain Validation detected failure, dropping back\n"); DV_SET(period, period); prevperiod = period; } return 1;}static intspi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer){ int l; /* first off do a test unit ready. This can error out * because of reservations or some other reason. If it * fails, the device won't let us write to the echo buffer * so just return failure */ const char spi_test_unit_ready[] = { TEST_UNIT_READY, 0, 0, 0, 0, 0 }; const char spi_read_buffer_descriptor[] = { READ_BUFFER, 0x0b, 0, 0, 0, 0, 0, 0, 4, 0 }; sreq->sr_cmd_len = 0; sreq->sr_data_direction = DMA_NONE; /* We send a set of three TURs to clear any outstanding * unit attention conditions if they exist (Otherwise the * buffer tests won't be happy). If the TUR still fails * (reservation conflict, device not ready, etc) just * skip the write tests */ for (l = 0; ; l++) { scsi_wait_req(sreq, spi_test_unit_ready, NULL, 0, DV_TIMEOUT, DV_RETRIES); if(sreq->sr_result) { if(l >= 3) return 0; } else { /* TUR succeeded */ break; } } sreq->sr_cmd_len = 0; sreq->sr_data_direction = DMA_FROM_DEVICE; scsi_wait_req(sreq, spi_read_buffer_descriptor, buffer, 4, DV_TIMEOUT, DV_RETRIES); if (sreq->sr_result) /* Device has no echo buffer */ return 0; return buffer[3] + ((buffer[2] & 0x1f) << 8);}static voidspi_dv_device_internal(struct scsi_request *sreq, u8 *buffer){ struct spi_internal *i = to_spi_internal(sreq->sr_host->transportt); struct scsi_device *sdev = sreq->sr_device; int len = sdev->inquiry_len; /* first set us up for narrow async */ DV_SET(offset, 0); DV_SET(width, 0); if (!spi_dv_device_compare_inquiry(sreq, buffer, buffer, DV_LOOPS)) { SPI_PRINTK(sdev, KERN_ERR, "Domain Validation Initial Inquiry Failed\n"); /* FIXME: should probably offline the device here? */ return; } /* test width */ if (i->f->set_width && sdev->wdtr) { i->f->set_width(sdev, 1); if (!spi_dv_device_compare_inquiry(sreq, buffer, buffer + len, DV_LOOPS)) { SPI_PRINTK(sdev, KERN_ERR, "Wide Transfers Fail\n"); i->f->set_width(sdev, 0); } } if (!i->f->set_period) return; /* device can't handle synchronous */ if(!sdev->ppr && !sdev->sdtr) return; /* now set up to the maximum */ DV_SET(offset, 255); DV_SET(period, 1); if (!spi_dv_retrain(sreq, buffer, buffer + len, spi_dv_device_compare_inquiry)) return; /* OK, now we have our initial speed set by the read only inquiry * test, now try an echo buffer test (if the device allows it) */ if ((len = spi_dv_device_get_echo_buffer(sreq, buffer)) == 0) { SPI_PRINTK(sdev, KERN_INFO, "Domain Validation skipping write tests\n"); return; } if (len > SPI_MAX_ECHO_BUFFER_SIZE) { SPI_PRINTK(sdev, KERN_WARNING, "Echo buffer size %d is too big, trimming to %d\n", len, SPI_MAX_ECHO_BUFFER_SIZE); len = SPI_MAX_ECHO_BUFFER_SIZE; } spi_dv_retrain(sreq, buffer, buffer + len, spi_dv_device_echo_buffer);}/** spi_dv_device - Do Domain Validation on the device * @sdev: scsi device to validate * * Performs the domain validation on the given device in the * current execution thread. Since DV operations may sleep, * the current thread must have user context. Also no SCSI * related locks that would deadlock I/O issued by the DV may * be held. */voidspi_dv_device(struct scsi_device *sdev){ struct scsi_request *sreq = scsi_allocate_request(sdev, GFP_KERNEL); u8 *buffer; const int len = SPI_MAX_ECHO_BUFFER_SIZE*2; if (unlikely(!sreq)) return; if (unlikely(scsi_device_get(sdev))) goto out_free_req; buffer = kmalloc(len, GFP_KERNEL); if (unlikely(!buffer)) goto out_put; memset(buffer, 0, len); if (unlikely(scsi_device_quiesce(sdev))) goto out_free; spi_dv_pending(sdev) = 1; down(&spi_dv_sem(sdev)); SPI_PRINTK(sdev, KERN_INFO, "Beginning Domain Validation\n"); spi_dv_device_internal(sreq, buffer); SPI_PRINTK(sdev, KERN_INFO, "Ending Domain Validation\n"); up(&spi_dv_sem(sdev)); spi_dv_pending(sdev) = 0; scsi_device_resume(sdev); out_free: kfree(buffer); out_put: scsi_device_put(sdev); out_free_req: scsi_release_request(sreq);}EXPORT_SYMBOL(spi_dv_device);struct work_queue_wrapper { struct work_struct work; struct scsi_device *sdev;};static voidspi_dv_device_work_wrapper(void *data){ struct work_queue_wrapper *wqw = (struct work_queue_wrapper *)data; struct scsi_device *sdev = wqw->sdev; kfree(wqw); spi_dv_device(sdev); spi_dv_pending(sdev) = 0; scsi_device_put(sdev);}/** * spi_schedule_dv_device - schedule domain validation to occur on the device * @sdev: The device to validate * * Identical to spi_dv_device() above, except that the DV will be * scheduled to occur in a workqueue later. All memory allocations * are atomic, so may be called from any context including those holding * SCSI locks. */voidspi_schedule_dv_device(struct scsi_device *sdev){ struct work_queue_wrapper *wqw = kmalloc(sizeof(struct work_queue_wrapper), GFP_ATOMIC); if (unlikely(!wqw)) return; if (unlikely(spi_dv_pending(sdev))) { kfree(wqw); return; } /* Set pending early (dv_device doesn't check it, only sets it) */ spi_dv_pending(sdev) = 1; if (unlikely(scsi_device_get(sdev))) { kfree(wqw); spi_dv_pending(sdev) = 0; return; } INIT_WORK(&wqw->work, spi_dv_device_work_wrapper, wqw); wqw->sdev = sdev; schedule_work(&wqw->work);}EXPORT_SYMBOL(spi_schedule_dv_device);#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]; \ if (i->f->show_##field) \ 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 (unlikely(!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); 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++] = &class_device_attr_revalidate; 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_DESCRIPTION("SPI Transport Attributes");MODULE_LICENSE("GPL");module_init(spi_transport_init);module_exit(spi_transport_exit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?