static ssize_t amzn_sfp_rw()

in drivers/misc/sfp/amzn-sfp.c [88:304]


static ssize_t amzn_sfp_rw(struct bin_attribute *ba, char *buf, loff_t ofs,
    size_t len, u16 flags)
{
	struct amzn_sfp_softc *sc = ba->private;
	struct i2c_client *client = sc->client;
	char iobuf[AMZN_SFP_HALF_SIZE + 1];
	struct i2c_msg msg[2];
	unsigned long ts;
	int error, nmsgs;
	u16 addr;
	u8 reg;

	/* Make sure the offset and length are valid. */
	if (ofs < 0 || ofs >= ba->size)
		return -ESPIPE;
	if (len == 0)
		return -EINVAL;
	if (ofs + len > ba->size)
		return -ENOSPC;

	addr = client->addr;

	rt_mutex_lock(&sc->lock);

	switch (sc->sfp_type) {
	case AMZN_SFP_TYPE_SFP_PLUS:
		/*
		 * Handle I2C address auto-increment for DOM access.
		 * Never cross-over between different I2C addresses!
		 */
		addr += ofs / AMZN_SFP_FULL_SIZE;
		reg = ofs % AMZN_SFP_FULL_SIZE;
		if (len + reg > AMZN_SFP_FULL_SIZE)
			len = AMZN_SFP_FULL_SIZE - reg;
		break;
	case AMZN_SFP_TYPE_QSFP_PLUS:
	case AMZN_SFP_TYPE_QSFP28:
	case AMZN_SFP_TYPE_QSFP_DD:
		/*
		 * Handle paging of the upper half.  Never cross-over
		 * into different pages or from lower into upper half.
		 * The lower half starts at offset 0. Page 0 of the
		 * upper half starts at offset 128. Upper half page 1
		 * starts at offset 256.  Upper half page X starts at
		 * offset 128 + X * 128.
		 */
		if (ofs < AMZN_SFP_HALF_SIZE) {
			/* Lower half: don't cross-over into upper half. */
			reg = ofs;
			if (len + reg > AMZN_SFP_HALF_SIZE)
				len = AMZN_SFP_HALF_SIZE - reg;
			break;
		}

		/* Upper half */

		/* Prepare the buffer for writing page select. */
		iobuf[0] = AMZN_QSFP_PAGE_SELECT;
		iobuf[1] = (ofs / AMZN_SFP_HALF_SIZE) - 1;

		/* Calculate the offset on the page. */
		reg = (ofs % AMZN_SFP_HALF_SIZE) + AMZN_SFP_HALF_SIZE;

		/* Stay within the page size (which starts at 128). */
		if (len + reg > AMZN_SFP_FULL_SIZE)
			len = AMZN_SFP_FULL_SIZE - reg;

		/*
		 * Ok, so now we have to be careful.  We can't just
		 * blindly write the page select register.  DACs in
		 * particular barf on that (NAK the data write).
		 * This even applies to writing a value 0 to select
		 * page 0.  As such, we should only write to the
		 * page select register when necessary.
		 * So, what we need to do is read the current value
		 * of the page select register and only select the
		 * page if the current page is different from the
		 * desired page.  But this would add overhead for
		 * each and every upper page access.
		 * To prevent the extra overhead, we cache the page
		 * in memory and give it a 1 second retention.  As
		 * such, we only read the page select register once
		 * a second to determine the active upper page.
		 * The retention is needed because modules are hot-
		 * pluggable.  We don't have the knowledge in this
		 * driver to know if the module we're talking to
		 * hasn't been replaced.
		 */
		ts = sc->cur_page_ts + amzn_sfp_page_retention * HZ;
		if (sc->cur_page == -1 ||
		    !time_in_range(jiffies, sc->cur_page_ts, ts)) {
			/*
			 * Read the page select register and update our
			 * notion of the current page.  Read the value
			 * in iobuf[2] to preserve the desired page in
			 * iobuf[1].
			 */
			nmsgs = 0;
			msg[nmsgs].addr = addr;
			msg[nmsgs].flags = 0;
			msg[nmsgs].len = 1;
			msg[nmsgs].buf = &iobuf[0];
			nmsgs++;
			msg[nmsgs].addr = addr;
			msg[nmsgs].flags = I2C_M_RD;
			msg[nmsgs].len = 1;
			msg[nmsgs].buf = &iobuf[2];
			nmsgs++;

			error = i2c_transfer(client->adapter, msg, nmsgs);
			if (error < 0) {
				/* Don't trust our state. */
				sc->cur_page = -1;
				rt_mutex_unlock(&sc->lock);
				return error;
			}

			if (iobuf[2] != sc->cur_page) {
				if (sc->cur_page != -1)
					dev_notice(&sc->client->dev,
					    "resetting current page to %u"
					    " (was %u)\n", iobuf[2],
					    sc->cur_page);
				sc->cur_page = iobuf[2];
			}
			sc->cur_page_ts = jiffies;
		}

		/*
		 * Don't write the page select register if the desired
		 * page is the same as the current page.
		 */
		if (iobuf[1] == sc->cur_page)
			break;

		/*
		 * Write the page select register.  We have a lock on
		 * the device, so we know the page can not be changed
		 * between now and when we access the page.
		 */
		nmsgs = 0;
		msg[nmsgs].addr = addr;
		msg[nmsgs].flags = 0;
		msg[nmsgs].len = 2;
		msg[nmsgs].buf = iobuf;
		nmsgs++;

		error = i2c_transfer(client->adapter, msg, nmsgs);
		if (error < 0) {
			/* Don't trust our state. */
			sc->cur_page = -1;
			rt_mutex_unlock(&sc->lock);
			return error;
		}

		sc->cur_page = iobuf[1];
		sc->cur_page_ts = jiffies;
		break;
	default:
		/*
		 * Unknown SFP type, we simply give access to the full
		 * EEPROM, without any knowledge about.  You get what
		 * you get...
		 */
		reg = ofs;
		break;
	}

	/*
	 * The SPI-I2C controller has a limited buffer
	 * size (96 bytes) and the driver simply returns EOPNOTSUPP when
	 * a larger transfer is requested.  Bad driver!
	 */
	if (len > 64)
		len = 64;

	nmsgs = 0;
	if (flags == I2C_M_RD) {
		msg[nmsgs].addr = addr;
		msg[nmsgs].flags = 0;
		msg[nmsgs].len = 1;
		msg[nmsgs].buf = &reg;
		nmsgs++;
		msg[nmsgs].addr = addr;
		msg[nmsgs].flags = flags;
		msg[nmsgs].len = len;
		msg[nmsgs].buf = buf;
		nmsgs++;
	} else {
		iobuf[0] = reg;
		memcpy(iobuf + 1, buf, len);
		msg[nmsgs].addr = addr;
		msg[nmsgs].flags = 0;
		msg[nmsgs].len = len + 1;
		msg[nmsgs].buf = iobuf;
		nmsgs++;
	}
	/* Writing to page select byte and immediate read to that same page 
	 * cause certain modules to hang. Wait 4 - 5ms for modules to load 
	 * upper page eeprom
	 */
	ts = sc->cur_page_ts + msecs_to_jiffies(amzn_sfp_page_load_wait_ms);
	if (time_in_range(jiffies, sc->cur_page_ts, ts)) {
		ts = amzn_sfp_page_load_wait_ms * 1000;
		usleep_range(ts, ts + 1000);
	}

	error = i2c_transfer(client->adapter, msg, nmsgs);

	rt_mutex_unlock(&sc->lock);

	if (error < 0)
		return error;
	if (error != nmsgs)
		return -EPIPE;
	return (ssize_t)len;
}