sdio: initial CIS parsing code
authorNicolas Pitre <nico@cam.org>
Sat, 16 Jun 2007 06:04:16 +0000 (02:04 -0400)
committerPierre Ossman <drzeus@drzeus.cx>
Sun, 23 Sep 2007 18:26:42 +0000 (20:26 +0200)
Signed-off-by: Nicolas Pitre <npitre@mvista.com>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
drivers/mmc/core/Makefile
drivers/mmc/core/sdio.c
drivers/mmc/core/sdio_cis.c [new file with mode: 0644]
drivers/mmc/core/sdio_cis.h [new file with mode: 0644]

index bf7a00248039cf225e86fcb20c8cc05b82c48490..05d69fc72c18ddafa37738a6b74953d9ac011439 100644 (file)
@@ -10,5 +10,5 @@ obj-$(CONFIG_MMC)             += mmc_core.o
 mmc_core-y                     := core.o sysfs.o bus.o host.o \
                                   mmc.o mmc_ops.o sd.o sd_ops.o \
                                   sdio.o sdio_ops.o sdio_bus.o \
-                                  sdio_io.o
+                                  sdio_cis.o sdio_io.o
 
index be623856f288cbb92e6cad85441639afc7ef4f8d..c5baf76146b2755e5d8bfd2b73a2e5c14306734c 100644 (file)
@@ -22,6 +22,7 @@
 #include "mmc_ops.h"
 #include "sd_ops.h"
 #include "sdio_ops.h"
+#include "sdio_cis.h"
 
 static int sdio_read_fbr(struct sdio_func *func)
 {
@@ -65,6 +66,10 @@ static int sdio_init_func(struct mmc_card *card, unsigned int fn)
        if (ret)
                goto fail;
 
+       ret = sdio_read_cis(func);
+       if (ret)
+               goto fail;
+
        card->sdio_func[fn - 1] = func;
 
        return 0;
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
new file mode 100644 (file)
index 0000000..114b600
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * linux/drivers/mmc/core/sdio_cis.c
+ *
+ * Author:     Nicolas Pitre
+ * Created:    June 11, 2007
+ * Copyright:  MontaVista Software Inc.
+ *
+ * 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
+ * your option) any later version.
+ */
+
+#include <linux/kernel.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "sdio_cis.h"
+#include "sdio_ops.h"
+
+static int cistpl_manfid(struct sdio_func *func,
+                        const unsigned char *buf,
+                        unsigned size)
+{
+       /* TPLMID_MANF */
+       func->vendor = buf[0] | (buf[1] << 8);
+
+       /* TPLMID_CARD */
+       func->device = buf[2] | (buf[3] << 8);
+
+       return 0;
+}
+
+struct cis_tpl {
+       unsigned char code;
+       unsigned char min_size;
+       int (*parse)(struct sdio_func *, const unsigned char *buf, unsigned size);
+};
+
+static const struct cis_tpl cis_tpl_list[] = {
+       {       0x15,   3,      /* cistpl_vers_1 */     },
+       {       0x20,   4,      cistpl_manfid           },
+       {       0x21,   2,      /* cistpl_funcid */     },
+       {       0x22,   0,      /* cistpl_funce */      },
+};
+
+int sdio_read_cis(struct sdio_func *func)
+{
+       int ret;
+       unsigned char *buf;
+       unsigned i, ptr = 0;
+
+       for (i = 0; i < 3; i++) {
+               unsigned char x;
+               ret = mmc_io_rw_direct(func->card, 0, 0,
+                               func->num * 0x100 + SDIO_FBR_CIS + i, 0, &x);
+               if (ret)
+                       return ret;
+               ptr |= x << (i * 8);
+       }
+
+       buf = kmalloc(256, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       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)
+                       break;
+
+               /* 0xff means we're done */
+               if (tpl_code == 0xff)
+                       break;
+
+               ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_link);
+               if (ret)
+                       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)) {
+                       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;
+                       break;
+               }
+
+               for (i = 0; i < tpl_link; i++) {
+                       ret = mmc_io_rw_direct(func->card, 0, 0, ptr + i, 0, &buf[i]);
+                       if (ret)
+                               break;
+               }
+               if (ret)
+                       break;
+               ptr += tpl_link;
+
+               if (tpl->parse)
+                       ret = tpl->parse(func, buf, tpl_link);
+       } while (!ret);
+
+       kfree(buf);
+       return ret;
+}
diff --git a/drivers/mmc/core/sdio_cis.h b/drivers/mmc/core/sdio_cis.h
new file mode 100644 (file)
index 0000000..df21c49
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * linux/drivers/mmc/core/sdio_cis.h
+ *
+ * Author:     Nicolas Pitre
+ * Created:    June 11, 2007
+ * Copyright:  MontaVista Software Inc.
+ *
+ * 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
+ * your option) any later version.
+ */
+
+#ifndef _MMC_SDIO_CIS_H
+#define _MMC_SDIO_CIS_H
+
+int sdio_read_cis(struct sdio_func *func);
+
+#endif