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