scsi_transport_spi.c

来自「linux 内核源代码」· C语言 代码 · 共 1,441 行 · 第 1/3 页

C
1,441
字号
	return retval;}static CLASS_DEVICE_ATTR(period, S_IRUGO | S_IWUSR, 			 show_spi_transport_period,			 store_spi_transport_period);static ssize_tshow_spi_transport_min_period(struct class_device *cdev, char *buf){	struct scsi_target *starget = transport_class_to_starget(cdev);	struct spi_transport_attrs *tp =		(struct spi_transport_attrs *)&starget->starget_data;	return show_spi_transport_period_helper(buf, tp->min_period);}static ssize_tstore_spi_transport_min_period(struct class_device *cdev, const char *buf,			    size_t count){	struct scsi_target *starget = transport_class_to_starget(cdev);	struct spi_transport_attrs *tp =		(struct spi_transport_attrs *)&starget->starget_data;	return store_spi_transport_period_helper(cdev, buf, count,						 &tp->min_period);}static CLASS_DEVICE_ATTR(min_period, S_IRUGO | S_IWUSR, 			 show_spi_transport_min_period,			 store_spi_transport_min_period);static ssize_t show_spi_host_signalling(struct class_device *cdev, char *buf){	struct Scsi_Host *shost = transport_class_to_shost(cdev);	struct spi_internal *i = to_spi_internal(shost->transportt);	if (i->f->get_signalling)		i->f->get_signalling(shost);	return sprintf(buf, "%s\n", spi_signal_to_string(spi_signalling(shost)));}static ssize_t store_spi_host_signalling(struct class_device *cdev,					 const char *buf, size_t count){	struct Scsi_Host *shost = transport_class_to_shost(cdev);	struct spi_internal *i = to_spi_internal(shost->transportt);	enum spi_signal_type type = spi_signal_to_value(buf);	if (type != SPI_SIGNAL_UNKNOWN)		i->f->set_signalling(shost, type);	return count;}static CLASS_DEVICE_ATTR(signalling, S_IRUGO | S_IWUSR,			 show_spi_host_signalling,			 store_spi_host_signalling);#define DV_SET(x, y)			\	if(i->f->set_##x)		\		i->f->set_##x(sdev->sdev_target, y)enum spi_compare_returns {	SPI_COMPARE_SUCCESS,	SPI_COMPARE_FAILURE,	SPI_COMPARE_SKIP_TEST,};/* This is for read/write Domain Validation:  If the device supports * an echo buffer, we do read/write tests to it */static enum spi_compare_returnsspi_dv_device_echo_buffer(struct scsi_device *sdev, u8 *buffer,			  u8 *ptr, const int retries){	int len = ptr - buffer;	int j, k, r, result;	unsigned int pattern = 0x0000ffff;	struct scsi_sense_hdr sshdr;	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++) {		result = spi_execute(sdev, spi_write_buffer, DMA_TO_DEVICE,				     buffer, len, &sshdr);		if(result || !scsi_device_online(sdev)) {			scsi_device_set_state(sdev, SDEV_QUIESCE);			if (scsi_sense_valid(&sshdr)			    && sshdr.sense_key == ILLEGAL_REQUEST			    /* INVALID FIELD IN CDB */			    && sshdr.asc == 0x24 && sshdr.ascq == 0x00)				/* This would mean that the drive lied				 * to us about supporting an echo				 * buffer (unfortunately some Western				 * Digital drives do precisely this)				 */				return SPI_COMPARE_SKIP_TEST;			sdev_printk(KERN_ERR, sdev, "Write Buffer failure %x\n", result);			return SPI_COMPARE_FAILURE;		}		memset(ptr, 0, len);		spi_execute(sdev, spi_read_buffer, DMA_FROM_DEVICE,			    ptr, len, NULL);		scsi_device_set_state(sdev, SDEV_QUIESCE);		if (memcmp(buffer, ptr, len) != 0)			return SPI_COMPARE_FAILURE;	}	return SPI_COMPARE_SUCCESS;}/* This is for the simplest form of Domain Validation: a read test * on the inquiry data from the device */static enum spi_compare_returnsspi_dv_device_compare_inquiry(struct scsi_device *sdev, u8 *buffer,			      u8 *ptr, const int retries){	int r, result;	const int len = sdev->inquiry_len;	const char spi_inquiry[] = {		INQUIRY, 0, 0, 0, len, 0	};	for (r = 0; r < retries; r++) {		memset(ptr, 0, len);		result = spi_execute(sdev, spi_inquiry, DMA_FROM_DEVICE,				     ptr, len, NULL);				if(result || !scsi_device_online(sdev)) {			scsi_device_set_state(sdev, SDEV_QUIESCE);			return SPI_COMPARE_FAILURE;		}		/* 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 SPI_COMPARE_FAILURE;	}	return SPI_COMPARE_SUCCESS;}static enum spi_compare_returnsspi_dv_retrain(struct scsi_device *sdev, u8 *buffer, u8 *ptr,	       enum spi_compare_returns 	       (*compare_fn)(struct scsi_device *, u8 *, u8 *, int)){	struct spi_internal *i = to_spi_internal(sdev->host->transportt);	struct scsi_target *starget = sdev->sdev_target;	int period = 0, prevperiod = 0; 	enum spi_compare_returns retval;	for (;;) {		int newperiod;		retval = compare_fn(sdev, buffer, ptr, DV_LOOPS);		if (retval == SPI_COMPARE_SUCCESS		    || retval == SPI_COMPARE_SKIP_TEST)			break;		/* OK, retrain, fallback */		if (i->f->get_iu)			i->f->get_iu(starget);		if (i->f->get_qas)			i->f->get_qas(starget);		if (i->f->get_period)			i->f->get_period(sdev->sdev_target);		/* Here's the fallback sequence; first try turning off		 * IU, then QAS (if we can control them), then finally		 * fall down the periods */		if (i->f->set_iu && spi_iu(starget)) {			starget_printk(KERN_ERR, starget, "Domain Validation Disabing Information Units\n");			DV_SET(iu, 0);		} else if (i->f->set_qas && spi_qas(starget)) {			starget_printk(KERN_ERR, starget, "Domain Validation Disabing Quick Arbitration and Selection\n");			DV_SET(qas, 0);		} else {			newperiod = spi_period(starget);			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 */				starget_printk(KERN_ERR, starget, "Domain Validation Failure, dropping back to Asynchronous\n");				DV_SET(offset, 0);				return SPI_COMPARE_FAILURE;			}			starget_printk(KERN_ERR, starget, "Domain Validation detected failure, dropping back\n");			DV_SET(period, period);			prevperiod = period;		}	}	return retval;}static intspi_dv_device_get_echo_buffer(struct scsi_device *sdev, u8 *buffer){	int l, result;	/* 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	};		/* 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++) {		result = spi_execute(sdev, spi_test_unit_ready, DMA_NONE, 				     NULL, 0, NULL);		if(result) {			if(l >= 3)				return 0;		} else {			/* TUR succeeded */			break;		}	}	result = spi_execute(sdev, spi_read_buffer_descriptor, 			     DMA_FROM_DEVICE, buffer, 4, NULL);	if (result)		/* Device has no echo buffer */		return 0;	return buffer[3] + ((buffer[2] & 0x1f) << 8);}static voidspi_dv_device_internal(struct scsi_device *sdev, u8 *buffer){	struct spi_internal *i = to_spi_internal(sdev->host->transportt);	struct scsi_target *starget = sdev->sdev_target;	struct Scsi_Host *shost = sdev->host;	int len = sdev->inquiry_len;	int min_period = spi_min_period(starget);	int max_width = spi_max_width(starget);	/* first set us up for narrow async */	DV_SET(offset, 0);	DV_SET(width, 0);	if (spi_dv_device_compare_inquiry(sdev, buffer, buffer, DV_LOOPS)	    != SPI_COMPARE_SUCCESS) {		starget_printk(KERN_ERR, starget, "Domain Validation Initial Inquiry Failed\n");		/* FIXME: should probably offline the device here? */		return;	}	if (!scsi_device_wide(sdev)) {		spi_max_width(starget) = 0;		max_width = 0;	}	/* test width */	if (i->f->set_width && max_width) {		i->f->set_width(starget, 1);		if (spi_dv_device_compare_inquiry(sdev, buffer,						   buffer + len,						   DV_LOOPS)		    != SPI_COMPARE_SUCCESS) {			starget_printk(KERN_ERR, starget, "Wide Transfers Fail\n");			i->f->set_width(starget, 0);			/* Make sure we don't force wide back on by asking			 * for a transfer period that requires it */			max_width = 0;			if (min_period < 10)				min_period = 10;		}	}	if (!i->f->set_period)		return;	/* device can't handle synchronous */	if (!scsi_device_sync(sdev) && !scsi_device_dt(sdev))		return;	/* len == -1 is the signal that we need to ascertain the	 * presence of an echo buffer before trying to use it.  len ==	 * 0 means we don't have an echo buffer */	len = -1; retry:	/* now set up to the maximum */	DV_SET(offset, spi_max_offset(starget));	DV_SET(period, min_period);	/* try QAS requests; this should be harmless to set if the	 * target supports it */	if (scsi_device_qas(sdev)) {		DV_SET(qas, 1);	} else {		DV_SET(qas, 0);	}	if (scsi_device_ius(sdev) && min_period < 9) {		/* This u320 (or u640). Set IU transfers */		DV_SET(iu, 1);		/* Then set the optional parameters */		DV_SET(rd_strm, 1);		DV_SET(wr_flow, 1);		DV_SET(rti, 1);		if (min_period == 8)			DV_SET(pcomp_en, 1);	} else {		DV_SET(iu, 0);	}	/* now that we've done all this, actually check the bus	 * signal type (if known).  Some devices are stupid on	 * a SE bus and still claim they can try LVD only settings */	if (i->f->get_signalling)		i->f->get_signalling(shost);	if (spi_signalling(shost) == SPI_SIGNAL_SE ||	    spi_signalling(shost) == SPI_SIGNAL_HVD ||	    !scsi_device_dt(sdev)) {		DV_SET(dt, 0);	} else {		DV_SET(dt, 1);	}	/* set width last because it will pull all the other	 * parameters down to required values */	DV_SET(width, max_width);	/* Do the read only INQUIRY tests */	spi_dv_retrain(sdev, buffer, buffer + sdev->inquiry_len,		       spi_dv_device_compare_inquiry);	/* See if we actually managed to negotiate and sustain DT */	if (i->f->get_dt)		i->f->get_dt(starget);	/* see if the device has an echo buffer.  If it does we can do	 * the SPI pattern write tests.  Because of some broken	 * devices, we *only* try this on a device that has actually	 * negotiated DT */	if (len == -1 && spi_dt(starget))		len = spi_dv_device_get_echo_buffer(sdev, buffer);	if (len <= 0) {		starget_printk(KERN_INFO, starget, "Domain Validation skipping write tests\n");		return;	}	if (len > SPI_MAX_ECHO_BUFFER_SIZE) {		starget_printk(KERN_WARNING, starget, "Echo buffer size %d is too big, trimming to %d\n", len, SPI_MAX_ECHO_BUFFER_SIZE);		len = SPI_MAX_ECHO_BUFFER_SIZE;	}	if (spi_dv_retrain(sdev, buffer, buffer + len,			   spi_dv_device_echo_buffer)	    == SPI_COMPARE_SKIP_TEST) {		/* OK, the stupid drive can't do a write echo buffer		 * test after all, fall back to the read tests */		len = 0;		goto retry;	}}/**	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_target *starget = sdev->sdev_target;	u8 *buffer;	const int len = SPI_MAX_ECHO_BUFFER_SIZE*2;	if (unlikely(scsi_device_get(sdev)))		return;	if (unlikely(spi_dv_in_progress(starget)))		return;	spi_dv_in_progress(starget) = 1;	buffer = kzalloc(len, GFP_KERNEL);	if (unlikely(!buffer))		goto out_put;	/* We need to verify that the actual device will quiesce; the	 * later target quiesce is just a nice to have */	if (unlikely(scsi_device_quiesce(sdev)))		goto out_free;	scsi_target_quiesce(starget);	spi_dv_pending(starget) = 1;	mutex_lock(&spi_dv_mutex(starget));	starget_printk(KERN_INFO, starget, "Beginning Domain Validation\n");	spi_dv_device_internal(sdev, buffer);	starget_printk(KERN_INFO, starget, "Ending Domain Validation\n");	mutex_unlock(&spi_dv_mutex(starget));	spi_dv_pending(starget) = 0;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?