Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4ec64960 authored by Albert Herranz's avatar Albert Herranz Committed by Linus Torvalds
Browse files

sdio: rework cis tuple parsing



Rework the current CIS tuple parsing code, reusing the existing
infrastructure and providing an easy way to add new CISTPL_FUNCE parsers
by TPLFE_TYPE.

Valid known CIS tuples are now silently queued for the SDIO function
driver when not parsed/processed (-EILSEQ) by the SDIO core.  Unknown CIS
tuples (-ENOENT) are queued too for the SDIO function driver without
aborting the initialization, but emit a warning in the kernel log.

CISTPL_FUNCE tuples can be "whitelisted" now by adding a matching entry to
the cis_tpl_funce_list table.

Signed-off-by: default avatarAlbert Herranz <albert_herranz@yahoo.es>
Acked-by: default avatarPierre Ossman <pierre@ossman.eu>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent c0521baf
Loading
Loading
Loading
Loading
+93 −74
Original line number Diff line number Diff line
@@ -97,26 +97,56 @@ static const unsigned char speed_val[16] =
static const unsigned int speed_unit[8] =
	{ 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };

/* FUNCE tuples with these types get passed to SDIO drivers */
static const unsigned char funce_type_whitelist[] = {
	4 /* CISTPL_FUNCE_LAN_NODE_ID used in Broadcom cards */

typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
			   const unsigned char *, unsigned);

struct cis_tpl {
	unsigned char code;
	unsigned char min_size;
	tpl_parse_t *parse;
};

static int cistpl_funce_whitelisted(unsigned char type)
static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func,
			 const char *tpl_descr,
			 const struct cis_tpl *tpl, int tpl_count,
			 unsigned char code,
			 const unsigned char *buf, unsigned size)
{
	int i;
	int i, ret;

	for (i = 0; i < ARRAY_SIZE(funce_type_whitelist); i++) {
		if (funce_type_whitelist[i] == type)
			return 1;
	/* look for a matching code in the table */
	for (i = 0; i < tpl_count; i++, tpl++) {
		if (tpl->code == code)
			break;
	}
	return 0;
	if (i < tpl_count) {
		if (size >= tpl->min_size) {
			if (tpl->parse)
				ret = tpl->parse(card, func, buf, size);
			else
				ret = -EILSEQ;	/* known tuple, not parsed */
		} else {
			/* invalid tuple */
			ret = -EINVAL;
		}
		if (ret && ret != -EILSEQ && ret != -ENOENT) {
			printk(KERN_ERR "%s: bad %s tuple 0x%02x (%u bytes)\n",
			       mmc_hostname(card->host), tpl_descr, code, size);
		}
	} else {
		/* unknown tuple */
		ret = -ENOENT;
	}

static int cistpl_funce_common(struct mmc_card *card,
	return ret;
}

static int cistpl_funce_common(struct mmc_card *card, struct sdio_func *func,
			       const unsigned char *buf, unsigned size)
{
	if (size < 0x04 || buf[0] != 0)
	/* Only valid for the common CIS (function 0) */
	if (func)
		return -EINVAL;

	/* TPLFE_FN0_BLK_SIZE */
@@ -129,20 +159,24 @@ static int cistpl_funce_common(struct mmc_card *card,
	return 0;
}

static int cistpl_funce_func(struct sdio_func *func,
static int cistpl_funce_func(struct mmc_card *card, struct sdio_func *func,
			     const unsigned char *buf, unsigned size)
{
	unsigned vsn;
	unsigned min_size;

	/* let SDIO drivers take care of whitelisted FUNCE tuples */
	if (cistpl_funce_whitelisted(buf[0]))
		return -EILSEQ;
	/* Only valid for the individual function's CIS (1-7) */
	if (!func)
		return -EINVAL;

	/*
	 * This tuple has a different length depending on the SDIO spec
	 * version.
	 */
	vsn = func->card->cccr.sdio_vsn;
	min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;

	if (size < min_size || buf[0] != 1)
	if (size < min_size)
		return -EINVAL;

	/* TPLFE_MAX_BLK_SIZE */
@@ -157,39 +191,32 @@ static int cistpl_funce_func(struct sdio_func *func,
	return 0;
}

static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
			const unsigned char *buf, unsigned size)
{
	int ret;

/*
	 * There should be two versions of the CISTPL_FUNCE tuple,
	 * one for the common CIS (function 0) and a version used by
	 * the individual function's CIS (1-7). Yet, the later has a
	 * different length depending on the SDIO spec version.
 * Known TPLFE_TYPEs table for CISTPL_FUNCE tuples.
 *
 * Note that, unlike PCMCIA, CISTPL_FUNCE tuples are not parsed depending
 * on the TPLFID_FUNCTION value of the previous CISTPL_FUNCID as on SDIO
 * TPLFID_FUNCTION is always hardcoded to 0x0C.
 */
	if (func)
		ret = cistpl_funce_func(func, buf, size);
	else
		ret = cistpl_funce_common(card, buf, size);
static const struct cis_tpl cis_tpl_funce_list[] = {
	{	0x00,	4,	cistpl_funce_common		},
	{	0x01,	0,	cistpl_funce_func		},
	{	0x04,	1+1+6,	/* CISTPL_FUNCE_LAN_NODE_ID */	},
};

	if (ret && ret != -EILSEQ) {
		printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u "
		       "type %u\n", mmc_hostname(card->host), size, buf[0]);
	}
static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
			const unsigned char *buf, unsigned size)
{
	if (size < 1)
		return -EINVAL;

	return ret;
	return cis_tpl_parse(card, func, "CISTPL_FUNCE",
			     cis_tpl_funce_list,
			     ARRAY_SIZE(cis_tpl_funce_list),
			     buf[0], buf, size);
}

typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
			   const unsigned char *, unsigned);

struct cis_tpl {
	unsigned char code;
	unsigned char min_size;
	tpl_parse_t *parse;
};

/* Known TPL_CODEs table for CIS tuples */
static const struct cis_tpl cis_tpl_list[] = {
	{	0x15,	3,	cistpl_vers_1		},
	{	0x20,	4,	cistpl_manfid		},
@@ -268,46 +295,38 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
			break;
		}

		for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
			if (cis_tpl_list[i].code == tpl_code)
				break;
		if (i < ARRAY_SIZE(cis_tpl_list)) {
			const struct cis_tpl *tpl = cis_tpl_list + i;
			if (tpl_link < tpl->min_size) {
				printk(KERN_ERR
				       "%s: bad CIS tuple 0x%02x"
				       " (length = %u, expected >= %u)\n",
				       mmc_hostname(card->host),
				       tpl_code, tpl_link, tpl->min_size);
				ret = -EINVAL;
			} else if (tpl->parse) {
				ret = tpl->parse(card, func,
						 this->data, tpl_link);
			}
		/* Try to parse the CIS tuple */
		ret = cis_tpl_parse(card, func, "CIS",
				    cis_tpl_list, ARRAY_SIZE(cis_tpl_list),
				    tpl_code, this->data, tpl_link);
		if (ret == -EILSEQ || ret == -ENOENT) {
			/*
			 * We don't need the tuple anymore if it was
			 * successfully parsed by the SDIO core or if it is
			 * not going to be parsed by SDIO drivers.
			 * The tuple is unknown or known but not parsed.
			 * Queue the tuple for the function driver.
			 */
			if (!ret || ret != -EILSEQ)
				kfree(this);
		} else {
			/* unknown tuple */
			ret = -EILSEQ;
		}

		if (ret == -EILSEQ) {
			/* this tuple is unknown to the core or whitelisted */
			this->next = NULL;
			this->code = tpl_code;
			this->size = tpl_link;
			*prev = this;
			prev = &this->next;
			printk(KERN_DEBUG
			       "%s: queuing CIS tuple 0x%02x length %u\n",
			       mmc_hostname(card->host), tpl_code, tpl_link);

			if (ret == -ENOENT) {
				/* warn about unknown tuples */
				printk(KERN_WARNING "%s: queuing unknown"
				       " CIS tuple 0x%02x (%u bytes)\n",
				       mmc_hostname(card->host),
				       tpl_code, tpl_link);
			}

			/* keep on analyzing tuples */
			ret = 0;
		} else {
			/*
			 * We don't need the tuple anymore if it was
			 * successfully parsed by the SDIO core or if it is
			 * not going to be queued for a driver.
			 */
			kfree(this);
		}

		ptr += tpl_link;