summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/core/sdio_bus.c3
-rw-r--r--drivers/mmc/core/sdio_cis.c83
-rw-r--r--drivers/mmc/core/sdio_cis.h1
-rw-r--r--include/linux/mmc/sdio_func.h12
4 files changed, 70 insertions, 29 deletions
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index fa488cea8594..78e0381f55ac 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -17,6 +17,7 @@
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h>
+#include "sdio_cis.h"
#include "sdio_bus.h"
#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
@@ -94,6 +95,8 @@ static void sdio_release_func(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
+ sdio_free_cis(func);
+
kfree(func);
}
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
index 114b600cd788..b6c7342572c1 100644
--- a/drivers/mmc/core/sdio_cis.c
+++ b/drivers/mmc/core/sdio_cis.c
@@ -5,6 +5,8 @@
* Created: June 11, 2007
* Copyright: MontaVista Software Inc.
*
+ * Copyright 2007 Pierre Ossman
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
@@ -49,7 +51,7 @@ static const struct cis_tpl cis_tpl_list[] = {
int sdio_read_cis(struct sdio_func *func)
{
int ret;
- unsigned char *buf;
+ struct sdio_func_tuple *this, **prev;
unsigned i, ptr = 0;
for (i = 0; i < 3; i++) {
@@ -61,13 +63,11 @@ int sdio_read_cis(struct sdio_func *func)
ptr |= x << (i * 8);
}
- buf = kmalloc(256, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ /* find the list tail */
+ for (prev = &func->tuples; *prev; prev = &(*prev)->next);
do {
unsigned char tpl_code, tpl_link;
- const struct cis_tpl *tpl;
ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_code);
if (ret)
@@ -81,39 +81,64 @@ int sdio_read_cis(struct sdio_func *func)
if (ret)
break;
- for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
- if (cis_tpl_list[i].code == tpl_code)
+ this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
+ if (!this)
+ return -ENOMEM;
+
+ for (i = 0; i < tpl_link; i++) {
+ ret = mmc_io_rw_direct(func->card, 0, 0,
+ ptr + i, 0, &this->data[i]);
+ if (ret)
break;
- if (i >= ARRAY_SIZE(cis_tpl_list)) {
- printk(KERN_WARNING
- "%s: unknown CIS tuple 0x%02x of length %u\n",
- sdio_func_id(func), tpl_code, tpl_link);
- ptr += tpl_link;
- continue;
}
- 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",
- sdio_func_id(func), tpl_code, tpl_link, tpl->min_size);
- ret = -EINVAL;
+ if (ret) {
+ kfree(this);
break;
}
- for (i = 0; i < tpl_link; i++) {
- ret = mmc_io_rw_direct(func->card, 0, 0, ptr + i, 0, &buf[i]);
- if (ret)
+ 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)) {
+ /* this tuple is unknown to the core */
+ 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",
+ sdio_func_id(func), tpl_code, tpl_link);
+ } else {
+ 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",
+ sdio_func_id(func), tpl_code, tpl_link, tpl->min_size);
+ ret = -EINVAL;
+ } else if (tpl->parse)
+ ret = tpl->parse(func, this->data, tpl_link);
+ kfree(this);
}
- if (ret)
- break;
- ptr += tpl_link;
- if (tpl->parse)
- ret = tpl->parse(func, buf, tpl_link);
+ ptr += tpl_link;
} while (!ret);
- kfree(buf);
return ret;
}
+
+void sdio_free_cis(struct sdio_func *func)
+{
+ struct sdio_func_tuple *tuple, *victim;
+
+ tuple = func->tuples;
+
+ while (tuple) {
+ victim = tuple;
+ tuple = tuple->next;
+ kfree(victim);
+ }
+
+ func->tuples = NULL;
+}
+
diff --git a/drivers/mmc/core/sdio_cis.h b/drivers/mmc/core/sdio_cis.h
index df21c495d133..863d3d516371 100644
--- a/drivers/mmc/core/sdio_cis.h
+++ b/drivers/mmc/core/sdio_cis.h
@@ -15,5 +15,6 @@
#define _MMC_SDIO_CIS_H
int sdio_read_cis(struct sdio_func *func);
+void sdio_free_cis(struct sdio_func *func);
#endif
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index 4164809a8e63..269067663c8d 100644
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -15,6 +15,16 @@
struct mmc_card;
/*
+ * SDIO function CIS tuple (unknown to the core)
+ */
+struct sdio_func_tuple {
+ struct sdio_func_tuple *next;
+ unsigned char code;
+ unsigned char size;
+ unsigned char data[0];
+};
+
+/*
* SDIO function devices
*/
struct sdio_func {
@@ -28,6 +38,8 @@ struct sdio_func {
unsigned int state; /* function state */
#define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */
+
+ struct sdio_func_tuple *tuples;
};
#define sdio_func_present(f) ((f)->state & SDIO_STATE_PRESENT)