static int br_pkt_phy_tx_prepare()

in regmap/regmap-spi-avmm.c [253:339]


static int br_pkt_phy_tx_prepare(struct spi_avmm_bridge *br)
{
	char *tb, *tb_end, *pb, *pb_limit, *pb_eop = NULL;
	unsigned int aligned_phy_len, move_size;
	bool need_esc = false;

	tb = br->trans_buf;
	tb_end = tb + br->trans_len;
	pb = br->phy_buf;
	pb_limit = pb + ARRAY_SIZE(br->phy_buf);

	*pb++ = PKT_SOP;

	/*
	 * The driver doesn't support multiple channels so the channel number
	 * is always 0.
	 */
	*pb++ = PKT_CHANNEL;
	*pb++ = 0x0;

	for (; pb < pb_limit && tb < tb_end; pb++) {
		if (need_esc) {
			*pb = *tb++ ^ 0x20;
			need_esc = false;
			continue;
		}

		/* EOP should be inserted before the last valid char */
		if (tb == tb_end - 1 && !pb_eop) {
			*pb = PKT_EOP;
			pb_eop = pb;
			continue;
		}

		/*
		 * insert an ESCAPE char if the data value equals any special
		 * char.
		 */
		switch (*tb) {
		case PKT_SOP:
		case PKT_EOP:
		case PKT_CHANNEL:
		case PKT_ESC:
			*pb = PKT_ESC;
			need_esc = true;
			break;
		case PHY_IDLE:
		case PHY_ESC:
			*pb = PHY_ESC;
			need_esc = true;
			break;
		default:
			*pb = *tb++;
			break;
		}
	}

	/* The phy buffer is used out but transaction layer data remains */
	if (tb < tb_end)
		return -ENOMEM;

	/* Store valid phy data length for spi transfer */
	br->phy_len = pb - br->phy_buf;

	if (br->word_len == 1)
		return 0;

	/* Do phy buf padding if word_len > 1 byte. */
	aligned_phy_len = ALIGN(br->phy_len, br->word_len);
	if (aligned_phy_len > sizeof(br->phy_buf))
		return -ENOMEM;

	if (aligned_phy_len == br->phy_len)
		return 0;

	/* move EOP and bytes after EOP to the end of aligned size */
	move_size = pb - pb_eop;
	memmove(&br->phy_buf[aligned_phy_len - move_size], pb_eop, move_size);

	/* fill the hole with PHY_IDLEs */
	memset(pb_eop, PHY_IDLE, aligned_phy_len - br->phy_len);

	/* update the phy data length */
	br->phy_len = aligned_phy_len;

	return 0;
}