summaryrefslogtreecommitdiffstats
path: root/sys/dev/pccard/pccard_cis.c
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>1999-10-26 06:52:31 +0000
committerimp <imp@FreeBSD.org>1999-10-26 06:52:31 +0000
commitc2e7296cf63567584fc5a4b7333448982213c4ac (patch)
tree535bb7fbe2f73f39b40647a29fbf0c12ced759b0 /sys/dev/pccard/pccard_cis.c
parentf4e14e481e52797ad7a9e862edfcab473b2644ce (diff)
downloadFreeBSD-src-c2e7296cf63567584fc5a4b7333448982213c4ac.zip
FreeBSD-src-c2e7296cf63567584fc5a4b7333448982213c4ac.tar.gz
Moderately hacked pccard code from newconfig. It is somewhat in
incomplete and likely has problem. The code was originally pcmcia, but I renamed it to pccard and made it compile on FreeBSD -current. I converted SIMPLEQ to STAILQ as well as a few sc->dev.xname -> device_printf changes. This is a green port of fairly mature code. I derived this work from the FreeBSD newconfig project (http://www.jp.freebsd.org/newconfig). Any problems with it are likely introduced by me. Obtained from: newconfig project
Diffstat (limited to 'sys/dev/pccard/pccard_cis.c')
-rw-r--r--sys/dev/pccard/pccard_cis.c1229
1 files changed, 1229 insertions, 0 deletions
diff --git a/sys/dev/pccard/pccard_cis.c b/sys/dev/pccard/pccard_cis.c
new file mode 100644
index 0000000..682ed58
--- /dev/null
+++ b/sys/dev/pccard/pccard_cis.c
@@ -0,0 +1,1229 @@
+/* $NetBSD: pcmcia_cis.c,v 1.10 1998/12/29 09:03:15 marc Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1997 Marc Horowitz. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Marc Horowitz.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/pccard/pccardreg.h>
+#include <dev/pccard/pccardchip.h>
+#include <dev/pccard/pccardvar.h>
+
+#ifdef PCCARDCISDEBUG
+int pccardcis_debug = 0;
+#define DPRINTF(arg) if (pccardcis_debug) printf arg
+#else
+#define DPRINTF(arg)
+#endif
+
+#define PCCARD_CIS_SIZE 1024
+
+struct cis_state {
+ int count;
+ int gotmfc;
+ struct pccard_config_entry temp_cfe;
+ struct pccard_config_entry *default_cfe;
+ struct pccard_card *card;
+ struct pccard_function *pf;
+};
+
+int pccard_parse_cis_tuple __P((struct pccard_tuple *, void *));
+
+void
+pccard_read_cis(sc)
+ struct pccard_softc *sc;
+{
+ struct cis_state state;
+
+ state.count = 0;
+ state.gotmfc = 0;
+
+ state.card = &sc->card;
+
+ state.card->error = 0;
+ state.card->cis1_major = -1;
+ state.card->cis1_minor = -1;
+ state.card->cis1_info[0] = NULL;
+ state.card->cis1_info[1] = NULL;
+ state.card->cis1_info[2] = NULL;
+ state.card->cis1_info[3] = NULL;
+ state.card->manufacturer = PCCARD_VENDOR_INVALID;
+ state.card->product = PCCARD_PRODUCT_INVALID;
+ STAILQ_INIT(&state.card->pf_head);
+
+ state.pf = NULL;
+
+ if (pccard_scan_cis((struct device *)sc, pccard_parse_cis_tuple,
+ &state) == -1)
+ state.card->error++;
+}
+
+int
+pccard_scan_cis(dev, fct, arg)
+ struct device *dev;
+ int (*fct) __P((struct pccard_tuple *, void *));
+ void *arg;
+{
+ struct pccard_softc *sc = (struct pccard_softc *) dev;
+ pccard_chipset_tag_t pct;
+ pccard_chipset_handle_t pch;
+ int window;
+ struct pccard_mem_handle pcmh;
+ struct pccard_tuple tuple;
+ int longlink_present;
+ int longlink_common;
+ u_long longlink_addr;
+ int mfc_count;
+ int mfc_index;
+ struct {
+ int common;
+ u_long addr;
+ } mfc[256 / 5];
+ int ret;
+
+ ret = 0;
+
+ pct = sc->pct;
+ pch = sc->pch;
+
+ /* allocate some memory */
+
+ if (pccard_chip_mem_alloc(pct, pch, PCCARD_CIS_SIZE, &pcmh)) {
+#ifdef DIAGNOSTIC
+ printf("%s: can't alloc memory to read attributes\n",
+ sc->dev.dv_xname);
+#endif
+ return -1;
+ }
+ tuple.memt = pcmh.memt;
+ tuple.memh = pcmh.memh;
+
+ /* initialize state for the primary tuple chain */
+ if (pccard_chip_mem_map(pct, pch, PCCARD_MEM_ATTR, 0,
+ PCCARD_CIS_SIZE, &pcmh, &tuple.ptr, &window)) {
+ pccard_chip_mem_free(pct, pch, &pcmh);
+#ifdef DIAGNOSTIC
+ printf("%s: can't map memory to read attributes\n",
+ sc->dev.dv_xname);
+#endif
+ return -1;
+ }
+#ifndef __FreeBSD__
+ DPRINTF(("cis mem map %x\n", (unsigned int) tuple.memh));
+#endif
+ tuple.mult = 2;
+
+ longlink_present = 1;
+ longlink_common = 1;
+ longlink_addr = 0;
+
+ mfc_count = 0;
+ mfc_index = 0;
+
+ DPRINTF(("%s: CIS tuple chain:\n", sc->dev.dv_xname));
+
+ while (1) {
+ while (1) {
+ /* get the tuple code */
+
+ tuple.code = pccard_cis_read_1(&tuple, tuple.ptr);
+
+ /* two special-case tuples */
+
+ if (tuple.code == PCCARD_CISTPL_NULL) {
+ DPRINTF(("CISTPL_NONE\n 00\n"));
+ tuple.ptr++;
+ continue;
+ } else if (tuple.code == PCCARD_CISTPL_END) {
+ DPRINTF(("CISTPL_END\n ff\n"));
+ /* Call the function for the END tuple, since
+ the CIS semantics depend on it */
+ if ((*fct) (&tuple, arg)) {
+ pccard_chip_mem_unmap(pct, pch,
+ window);
+ ret = 1;
+ goto done;
+ }
+ tuple.ptr++;
+ break;
+ }
+ /* now all the normal tuples */
+
+ tuple.length = pccard_cis_read_1(&tuple, tuple.ptr + 1);
+ switch (tuple.code) {
+ case PCCARD_CISTPL_LONGLINK_A:
+ case PCCARD_CISTPL_LONGLINK_C:
+ if (tuple.length < 4) {
+ DPRINTF(("CISTPL_LONGLINK_%s too "
+ "short %d\n",
+ longlink_common ? "C" : "A",
+ tuple.length));
+ break;
+ }
+ longlink_present = 1;
+ longlink_common = (tuple.code ==
+ PCCARD_CISTPL_LONGLINK_C) ? 1 : 0;
+ longlink_addr = pccard_tuple_read_4(&tuple, 0);
+ DPRINTF(("CISTPL_LONGLINK_%s %lx\n",
+ longlink_common ? "C" : "A",
+ longlink_addr));
+ break;
+ case PCCARD_CISTPL_NO_LINK:
+ longlink_present = 0;
+ DPRINTF(("CISTPL_NO_LINK\n"));
+ break;
+ case PCCARD_CISTPL_CHECKSUM:
+ if (tuple.length < 5) {
+ DPRINTF(("CISTPL_CHECKSUM too "
+ "short %d\n", tuple.length));
+ break;
+ } {
+ int16_t offset;
+ u_long addr, length;
+ u_int cksum, sum;
+ int i;
+
+ *((u_int16_t *) & offset) =
+ pccard_tuple_read_2(&tuple, 0);
+ length = pccard_tuple_read_2(&tuple, 2);
+ cksum = pccard_tuple_read_1(&tuple, 4);
+
+ addr = tuple.ptr + offset;
+
+ DPRINTF(("CISTPL_CHECKSUM addr=%lx "
+ "len=%lx cksum=%x",
+ addr, length, cksum));
+
+ /*
+ * XXX do more work to deal with
+ * distant regions
+ */
+ if ((addr >= PCCARD_CIS_SIZE) ||
+ ((addr + length) < 0) ||
+ ((addr + length) >=
+ PCCARD_CIS_SIZE)) {
+ DPRINTF((" skipped, "
+ "too distant\n"));
+ break;
+ }
+ sum = 0;
+ for (i = 0; i < length; i++)
+ sum +=
+ bus_space_read_1(tuple.memt,
+ tuple.memh,
+ addr + tuple.mult * i);
+ if (cksum != (sum & 0xff)) {
+ DPRINTF((" failed sum=%x\n",
+ sum));
+#if XXX
+ printf("%s: CIS checksum "
+ "failed\n",
+ sc->dev.dv_xname);
+#endif
+#if 0
+ /*
+ * XXX Some working cards have
+ * XXX bad checksums!!
+ */
+ ret = -1;
+#endif
+ } else {
+ DPRINTF((" ok\n"));
+ }
+ }
+ break;
+ case PCCARD_CISTPL_LONGLINK_MFC:
+ if (tuple.length < 1) {
+ DPRINTF(("CISTPL_LONGLINK_MFC too "
+ "short %d\n", tuple.length));
+ break;
+ }
+ /*
+ * this is kind of ad hoc, as I don't have
+ * any real documentation
+ */
+ {
+ int i;
+
+ mfc_count =
+ pccard_tuple_read_1(&tuple, 0);
+ DPRINTF(("CISTPL_LONGLINK_MFC %d",
+ mfc_count));
+ for (i = 0; i < mfc_count; i++) {
+ mfc[i].common =
+ (pccard_tuple_read_1(&tuple,
+ 1 + 5 * i) ==
+ PCCARD_MFC_MEM_COMMON) ?
+ 1 : 0;
+ mfc[i].addr =
+ pccard_tuple_read_4(&tuple,
+ 1 + 5 * i + 1);
+ DPRINTF((" %s:%lx",
+ mfc[i].common ? "common" :
+ "attr", mfc[i].addr));
+ }
+ DPRINTF(("\n"));
+ }
+ /*
+ * for LONGLINK_MFC, fall through to the
+ * function. This tuple has structural and
+ * semantic content.
+ */
+ default:
+ {
+ if ((*fct) (&tuple, arg)) {
+ pccard_chip_mem_unmap(pct,
+ pch, window);
+ ret = 1;
+ goto done;
+ }
+ }
+ break;
+ } /* switch */
+#ifdef PCCARDCISDEBUG
+ /* print the tuple */
+ {
+ int i;
+
+ DPRINTF((" %02x %02x", tuple.code,
+ tuple.length));
+
+ for (i = 0; i < tuple.length; i++) {
+ DPRINTF((" %02x",
+ pccard_tuple_read_1(&tuple, i)));
+ if ((i % 16) == 13)
+ DPRINTF(("\n"));
+ }
+ if ((i % 16) != 14)
+ DPRINTF(("\n"));
+ }
+#endif
+ /* skip to the next tuple */
+ tuple.ptr += 2 + tuple.length;
+ }
+
+ /*
+ * the chain is done. Clean up and move onto the next one,
+ * if any. The loop is here in the case that there is an MFC
+ * card with no longlink (which defaults to existing, == 0).
+ * In general, this means that if one pointer fails, it will
+ * try the next one, instead of just bailing.
+ */
+
+ while (1) {
+ pccard_chip_mem_unmap(pct, pch, window);
+
+ if (longlink_present) {
+ /*
+ * if the longlink is to attribute memory,
+ * then it is unindexed. That is, if the
+ * link value is 0x100, then the actual
+ * memory address is 0x200. This means that
+ * we need to multiply by 2 before calling
+ * mem_map, and then divide the resulting ptr
+ * by 2 after.
+ */
+
+ if (!longlink_common)
+ longlink_addr *= 2;
+
+ pccard_chip_mem_map(pct, pch, longlink_common ?
+ PCCARD_MEM_COMMON : PCCARD_MEM_ATTR,
+ longlink_addr, PCCARD_CIS_SIZE,
+ &pcmh, &tuple.ptr, &window);
+
+ if (!longlink_common)
+ tuple.ptr /= 2;
+#ifndef __FreeBSD__
+ DPRINTF(("cis mem map %x\n",
+ (unsigned int) tuple.memh));
+#endif
+ tuple.mult = longlink_common ? 1 : 2;
+ longlink_present = 0;
+ longlink_common = 1;
+ longlink_addr = 0;
+ } else if (mfc_count && (mfc_index < mfc_count)) {
+ if (!mfc[mfc_index].common)
+ mfc[mfc_index].addr *= 2;
+
+ pccard_chip_mem_map(pct, pch,
+ mfc[mfc_index].common ?
+ PCCARD_MEM_COMMON : PCCARD_MEM_ATTR,
+ mfc[mfc_index].addr, PCCARD_CIS_SIZE,
+ &pcmh, &tuple.ptr, &window);
+
+ if (!mfc[mfc_index].common)
+ tuple.ptr /= 2;
+#ifndef __FreeBSD__
+ DPRINTF(("cis mem map %x\n",
+ (unsigned int) tuple.memh));
+#endif
+ /* set parse state, and point at the next one */
+
+ tuple.mult = mfc[mfc_index].common ? 1 : 2;
+
+ mfc_index++;
+ } else {
+ goto done;
+ }
+
+ /* make sure that the link is valid */
+ tuple.code = pccard_cis_read_1(&tuple, tuple.ptr);
+ if (tuple.code != PCCARD_CISTPL_LINKTARGET) {
+ DPRINTF(("CISTPL_LINKTARGET expected, "
+ "code %02x observed\n", tuple.code));
+ continue;
+ }
+ tuple.length = pccard_cis_read_1(&tuple, tuple.ptr + 1);
+ if (tuple.length < 3) {
+ DPRINTF(("CISTPL_LINKTARGET too short %d\n",
+ tuple.length));
+ continue;
+ }
+ if ((pccard_tuple_read_1(&tuple, 0) != 'C') ||
+ (pccard_tuple_read_1(&tuple, 1) != 'I') ||
+ (pccard_tuple_read_1(&tuple, 2) != 'S')) {
+ DPRINTF(("CISTPL_LINKTARGET magic "
+ "%02x%02x%02x incorrect\n",
+ pccard_tuple_read_1(&tuple, 0),
+ pccard_tuple_read_1(&tuple, 1),
+ pccard_tuple_read_1(&tuple, 2)));
+ continue;
+ }
+ tuple.ptr += 2 + tuple.length;
+
+ break;
+ }
+ }
+
+ pccard_chip_mem_unmap(pct, pch, window);
+
+done:
+ /* Last, free the allocated memory block */
+ pccard_chip_mem_free(pct, pch, &pcmh);
+
+ return (ret);
+}
+
+/* XXX this is incredibly verbose. Not sure what trt is */
+
+void
+pccard_print_cis(device_t dev)
+{
+ struct pccard_softc *sc = (struct pccard_softc *) device_get_softc(dev);
+ struct pccard_card *card = &sc->card;
+ struct pccard_function *pf;
+ struct pccard_config_entry *cfe;
+ int i;
+
+ device_printf(dev, "CIS version ");
+ if (card->cis1_major == 4) {
+ if (card->cis1_minor == 0)
+ printf("PCCARD 1.0\n");
+ else if (card->cis1_minor == 1)
+ printf("PCCARD 2.0 or 2.1\n");
+ } else if (card->cis1_major >= 5)
+ printf("PC Card Standard %d.%d\n", card->cis1_major, card->cis1_minor);
+ else
+ printf("unknown (major=%d, minor=%d)\n",
+ card->cis1_major, card->cis1_minor);
+
+ device_printf(dev, "CIS info: ");
+ for (i = 0; i < 4; i++) {
+ if (card->cis1_info[i] == NULL)
+ break;
+ if (i)
+ printf(", ");
+ printf("%s", card->cis1_info[i]);
+ }
+ printf("\n");
+
+ device_printf(dev, "Manufacturer code 0x%x, product 0x%x\n",
+ card->manufacturer, card->product);
+
+ STAILQ_FOREACH(pf, &card->pf_head, pf_list) {
+ device_printf(dev, "function %d: ", pf->number);
+
+ switch (pf->function) {
+ case PCCARD_FUNCTION_UNSPEC:
+ printf("unspecified");
+ break;
+ case PCCARD_FUNCTION_MULTIFUNCTION:
+ printf("multi-function");
+ break;
+ case PCCARD_FUNCTION_MEMORY:
+ printf("memory");
+ break;
+ case PCCARD_FUNCTION_SERIAL:
+ printf("serial port");
+ break;
+ case PCCARD_FUNCTION_PARALLEL:
+ printf("parallel port");
+ break;
+ case PCCARD_FUNCTION_DISK:
+ printf("fixed disk");
+ break;
+ case PCCARD_FUNCTION_VIDEO:
+ printf("video adapter");
+ break;
+ case PCCARD_FUNCTION_NETWORK:
+ printf("network adapter");
+ break;
+ case PCCARD_FUNCTION_AIMS:
+ printf("auto incrementing mass storage");
+ break;
+ case PCCARD_FUNCTION_SCSI:
+ printf("SCSI bridge");
+ break;
+ case PCCARD_FUNCTION_SECURITY:
+ printf("Security services");
+ break;
+ case PCCARD_FUNCTION_INSTRUMENT:
+ printf("Instrument");
+ break;
+ default:
+ printf("unknown (%d)", pf->function);
+ break;
+ }
+
+ printf(", ccr addr %lx mask %lx\n", pf->ccr_base, pf->ccr_mask);
+
+ STAILQ_FOREACH(cfe, &pf->cfe_head, cfe_list) {
+ device_printf(dev, "function %d, config table entry "
+ "%d: ", pf->number, cfe->number);
+
+ switch (cfe->iftype) {
+ case PCCARD_IFTYPE_MEMORY:
+ printf("memory card");
+ break;
+ case PCCARD_IFTYPE_IO:
+ printf("I/O card");
+ break;
+ default:
+ printf("card type unknown");
+ break;
+ }
+
+ printf("; irq mask %x", cfe->irqmask);
+
+ if (cfe->num_iospace) {
+ printf("; iomask %lx, iospace", cfe->iomask);
+
+ for (i = 0; i < cfe->num_iospace; i++)
+ printf(" %lx%s%lx",
+ cfe->iospace[i].start,
+ cfe->iospace[i].length ? "-" : "",
+ cfe->iospace[i].start +
+ cfe->iospace[i].length - 1);
+ }
+ if (cfe->num_memspace) {
+ printf("; memspace");
+
+ for (i = 0; i < cfe->num_memspace; i++)
+ printf(" %lx%s%lx%s%lx",
+ cfe->memspace[i].cardaddr,
+ cfe->memspace[i].length ? "-" : "",
+ cfe->memspace[i].cardaddr +
+ cfe->memspace[i].length - 1,
+ cfe->memspace[i].hostaddr ?
+ "@" : "",
+ cfe->memspace[i].hostaddr);
+ }
+ if (cfe->maxtwins)
+ printf("; maxtwins %d", cfe->maxtwins);
+
+ printf(";");
+
+ if (cfe->flags & PCCARD_CFE_MWAIT_REQUIRED)
+ printf(" mwait_required");
+ if (cfe->flags & PCCARD_CFE_RDYBSY_ACTIVE)
+ printf(" rdybsy_active");
+ if (cfe->flags & PCCARD_CFE_WP_ACTIVE)
+ printf(" wp_active");
+ if (cfe->flags & PCCARD_CFE_BVD_ACTIVE)
+ printf(" bvd_active");
+ if (cfe->flags & PCCARD_CFE_IO8)
+ printf(" io8");
+ if (cfe->flags & PCCARD_CFE_IO16)
+ printf(" io16");
+ if (cfe->flags & PCCARD_CFE_IRQSHARE)
+ printf(" irqshare");
+ if (cfe->flags & PCCARD_CFE_IRQPULSE)
+ printf(" irqpulse");
+ if (cfe->flags & PCCARD_CFE_IRQLEVEL)
+ printf(" irqlevel");
+ if (cfe->flags & PCCARD_CFE_POWERDOWN)
+ printf(" powerdown");
+ if (cfe->flags & PCCARD_CFE_READONLY)
+ printf(" readonly");
+ if (cfe->flags & PCCARD_CFE_AUDIO)
+ printf(" audio");
+
+ printf("\n");
+ }
+ }
+
+ if (card->error)
+ device_printf(dev, "%d errors found while parsing CIS\n",
+ card->error);
+}
+
+int
+pccard_parse_cis_tuple(tuple, arg)
+ struct pccard_tuple *tuple;
+ void *arg;
+{
+ /* most of these are educated guesses */
+ static struct pccard_config_entry init_cfe = {
+ -1, PCCARD_CFE_RDYBSY_ACTIVE | PCCARD_CFE_WP_ACTIVE |
+ PCCARD_CFE_BVD_ACTIVE, PCCARD_IFTYPE_MEMORY,
+ };
+
+ struct cis_state *state = arg;
+
+ switch (tuple->code) {
+ case PCCARD_CISTPL_END:
+ /* if we've seen a LONGLINK_MFC, and this is the first
+ * END after it, reset the function list.
+ *
+ * XXX This might also be the right place to start a
+ * new function, but that assumes that a function
+ * definition never crosses any longlink, and I'm not
+ * sure about that. This is probably safe for MFC
+ * cards, but what we have now isn't broken, so I'd
+ * rather not change it.
+ */
+ if (state->gotmfc == 1) {
+ struct pccard_function *pf, *pfnext;
+
+ for (pf = STAILQ_FIRST(&state->card->pf_head);
+ pf != NULL; pf = pfnext) {
+ pfnext = STAILQ_NEXT(pf, pf_list);
+ free(pf, M_DEVBUF);
+ }
+
+ STAILQ_INIT(&state->card->pf_head);
+
+ state->count = 0;
+ state->gotmfc = 2;
+ state->pf = NULL;
+ }
+ break;
+ case PCCARD_CISTPL_LONGLINK_MFC:
+ /*
+ * this tuple's structure was dealt with in scan_cis. here,
+ * record the fact that the MFC tuple was seen, so that
+ * functions declared before the MFC link can be cleaned
+ * up.
+ */
+ state->gotmfc = 1;
+ break;
+#ifdef PCCARDCISDEBUG
+ case PCCARD_CISTPL_DEVICE:
+ case PCCARD_CISTPL_DEVICE_A:
+ {
+ u_int reg, dtype, dspeed;
+
+ reg = pccard_tuple_read_1(tuple, 0);
+ dtype = reg & PCCARD_DTYPE_MASK;
+ dspeed = reg & PCCARD_DSPEED_MASK;
+
+ DPRINTF(("CISTPL_DEVICE%s type=",
+ (tuple->code == PCCARD_CISTPL_DEVICE) ? "" : "_A"));
+ switch (dtype) {
+ case PCCARD_DTYPE_NULL:
+ DPRINTF(("null"));
+ break;
+ case PCCARD_DTYPE_ROM:
+ DPRINTF(("rom"));
+ break;
+ case PCCARD_DTYPE_OTPROM:
+ DPRINTF(("otprom"));
+ break;
+ case PCCARD_DTYPE_EPROM:
+ DPRINTF(("eprom"));
+ break;
+ case PCCARD_DTYPE_EEPROM:
+ DPRINTF(("eeprom"));
+ break;
+ case PCCARD_DTYPE_FLASH:
+ DPRINTF(("flash"));
+ break;
+ case PCCARD_DTYPE_SRAM:
+ DPRINTF(("sram"));
+ break;
+ case PCCARD_DTYPE_DRAM:
+ DPRINTF(("dram"));
+ break;
+ case PCCARD_DTYPE_FUNCSPEC:
+ DPRINTF(("funcspec"));
+ break;
+ case PCCARD_DTYPE_EXTEND:
+ DPRINTF(("extend"));
+ break;
+ default:
+ DPRINTF(("reserved"));
+ break;
+ }
+ DPRINTF((" speed="));
+ switch (dspeed) {
+ case PCCARD_DSPEED_NULL:
+ DPRINTF(("null"));
+ break;
+ case PCCARD_DSPEED_250NS:
+ DPRINTF(("250ns"));
+ break;
+ case PCCARD_DSPEED_200NS:
+ DPRINTF(("200ns"));
+ break;
+ case PCCARD_DSPEED_150NS:
+ DPRINTF(("150ns"));
+ break;
+ case PCCARD_DSPEED_100NS:
+ DPRINTF(("100ns"));
+ break;
+ case PCCARD_DSPEED_EXT:
+ DPRINTF(("ext"));
+ break;
+ default:
+ DPRINTF(("reserved"));
+ break;
+ }
+ }
+ DPRINTF(("\n"));
+ break;
+#endif
+ case PCCARD_CISTPL_VERS_1:
+ if (tuple->length < 6) {
+ DPRINTF(("CISTPL_VERS_1 too short %d\n",
+ tuple->length));
+ break;
+ } {
+ int start, i, ch, count;
+
+ state->card->cis1_major = pccard_tuple_read_1(tuple, 0);
+ state->card->cis1_minor = pccard_tuple_read_1(tuple, 1);
+
+ for (count = 0, start = 0, i = 0;
+ (count < 4) && ((i + 4) < 256); i++) {
+ ch = pccard_tuple_read_1(tuple, 2 + i);
+ if (ch == 0xff)
+ break;
+ state->card->cis1_info_buf[i] = ch;
+ if (ch == 0) {
+ state->card->cis1_info[count] =
+ state->card->cis1_info_buf + start;
+ start = i + 1;
+ count++;
+ }
+ }
+ DPRINTF(("CISTPL_VERS_1\n"));
+ }
+ break;
+ case PCCARD_CISTPL_MANFID:
+ if (tuple->length < 4) {
+ DPRINTF(("CISTPL_MANFID too short %d\n",
+ tuple->length));
+ break;
+ }
+ state->card->manufacturer = pccard_tuple_read_2(tuple, 0);
+ state->card->product = pccard_tuple_read_2(tuple, 2);
+ DPRINTF(("CISTPL_MANFID\n"));
+ break;
+ case PCCARD_CISTPL_FUNCID:
+ if (tuple->length < 1) {
+ DPRINTF(("CISTPL_FUNCID too short %d\n",
+ tuple->length));
+ break;
+ }
+ if ((state->pf == NULL) || (state->gotmfc == 2)) {
+ state->pf = malloc(sizeof(*state->pf), M_DEVBUF,
+ M_NOWAIT);
+ bzero(state->pf, sizeof(*state->pf));
+ state->pf->number = state->count++;
+ state->pf->last_config_index = -1;
+ STAILQ_INIT(&state->pf->cfe_head);
+
+ STAILQ_INSERT_TAIL(&state->card->pf_head, state->pf,
+ pf_list);
+ }
+ state->pf->function = pccard_tuple_read_1(tuple, 0);
+
+ DPRINTF(("CISTPL_FUNCID\n"));
+ break;
+ case PCCARD_CISTPL_CONFIG:
+ if (tuple->length < 3) {
+ DPRINTF(("CISTPL_CONFIG too short %d\n",
+ tuple->length));
+ break;
+ } {
+ u_int reg, rasz, rmsz, rfsz;
+ int i;
+
+ reg = pccard_tuple_read_1(tuple, 0);
+ rasz = 1 + ((reg & PCCARD_TPCC_RASZ_MASK) >>
+ PCCARD_TPCC_RASZ_SHIFT);
+ rmsz = 1 + ((reg & PCCARD_TPCC_RMSZ_MASK) >>
+ PCCARD_TPCC_RMSZ_SHIFT);
+ rfsz = ((reg & PCCARD_TPCC_RFSZ_MASK) >>
+ PCCARD_TPCC_RFSZ_SHIFT);
+
+ if (tuple->length < (rasz + rmsz + rfsz)) {
+ DPRINTF(("CISTPL_CONFIG (%d,%d,%d) too "
+ "short %d\n", rasz, rmsz, rfsz,
+ tuple->length));
+ break;
+ }
+ if (state->pf == NULL) {
+ state->pf = malloc(sizeof(*state->pf),
+ M_DEVBUF, M_NOWAIT);
+ bzero(state->pf, sizeof(*state->pf));
+ state->pf->number = state->count++;
+ state->pf->last_config_index = -1;
+ STAILQ_INIT(&state->pf->cfe_head);
+
+ STAILQ_INSERT_TAIL(&state->card->pf_head,
+ state->pf, pf_list);
+
+ state->pf->function = PCCARD_FUNCTION_UNSPEC;
+ }
+ state->pf->last_config_index =
+ pccard_tuple_read_1(tuple, 1);
+
+ state->pf->ccr_base = 0;
+ for (i = 0; i < rasz; i++)
+ state->pf->ccr_base |=
+ ((pccard_tuple_read_1(tuple, 2 + i)) <<
+ (i * 8));
+
+ state->pf->ccr_mask = 0;
+ for (i = 0; i < rmsz; i++)
+ state->pf->ccr_mask |=
+ ((pccard_tuple_read_1(tuple,
+ 2 + rasz + i)) << (i * 8));
+
+ /* skip the reserved area and subtuples */
+
+ /* reset the default cfe for each cfe list */
+ state->temp_cfe = init_cfe;
+ state->default_cfe = &state->temp_cfe;
+ }
+ DPRINTF(("CISTPL_CONFIG\n"));
+ break;
+ case PCCARD_CISTPL_CFTABLE_ENTRY:
+ {
+ int idx, i, j;
+ u_int reg, reg2;
+ u_int intface, def, num;
+ u_int power, timing, iospace, irq, memspace, misc;
+ struct pccard_config_entry *cfe;
+
+ idx = 0;
+
+ reg = pccard_tuple_read_1(tuple, idx);
+ idx++;
+ intface = reg & PCCARD_TPCE_INDX_INTFACE;
+ def = reg & PCCARD_TPCE_INDX_DEFAULT;
+ num = reg & PCCARD_TPCE_INDX_NUM_MASK;
+
+ /*
+ * this is a little messy. Some cards have only a
+ * cfentry with the default bit set. So, as we go
+ * through the list, we add new indexes to the queue,
+ * and keep a pointer to the last one with the
+ * default bit set. if we see a record with the same
+ * index, as the default, we stash the default and
+ * replace the queue entry. otherwise, we just add
+ * new entries to the queue, pointing the default ptr
+ * at them if the default bit is set. if we get to
+ * the end with the default pointer pointing at a
+ * record which hasn't had a matching index, that's
+ * ok; it just becomes a cfentry like any other.
+ */
+
+ /*
+ * if the index in the cis differs from the default
+ * cis, create new entry in the queue and start it
+ * with the current default
+ */
+ if (num != state->default_cfe->number) {
+ cfe = (struct pccard_config_entry *)
+ malloc(sizeof(*cfe), M_DEVBUF, M_NOWAIT);
+
+ *cfe = *state->default_cfe;
+
+ STAILQ_INSERT_TAIL(&state->pf->cfe_head,
+ cfe, cfe_list);
+
+ cfe->number = num;
+
+ /*
+ * if the default bit is set in the cis, then
+ * point the new default at whatever is being
+ * filled in
+ */
+ if (def)
+ state->default_cfe = cfe;
+ } else {
+ /*
+ * the cis index matches the default index,
+ * fill in the default cfentry. It is
+ * assumed that the cfdefault index is in the
+ * queue. For it to be otherwise, the cis
+ * index would have to be -1 (initial
+ * condition) which is not possible, or there
+ * would have to be a preceding cis entry
+ * which had the same cis index and had the
+ * default bit unset. Neither condition
+ * should happen. If it does, this cfentry
+ * is lost (written into temp space), which
+ * is an acceptable failure mode.
+ */
+
+ cfe = state->default_cfe;
+
+ /*
+ * if the cis entry does not have the default
+ * bit set, copy the default out of the way
+ * first.
+ */
+ if (!def) {
+ state->temp_cfe = *state->default_cfe;
+ state->default_cfe = &state->temp_cfe;
+ }
+ }
+
+ if (intface) {
+ reg = pccard_tuple_read_1(tuple, idx);
+ idx++;
+ if (reg & PCCARD_TPCE_IF_MWAIT)
+ cfe->flags |= PCCARD_CFE_MWAIT_REQUIRED;
+ if (reg & PCCARD_TPCE_IF_RDYBSY)
+ cfe->flags |= PCCARD_CFE_RDYBSY_ACTIVE;
+ if (reg & PCCARD_TPCE_IF_WP)
+ cfe->flags |= PCCARD_CFE_WP_ACTIVE;
+ if (reg & PCCARD_TPCE_IF_BVD)
+ cfe->flags |= PCCARD_CFE_BVD_ACTIVE;
+ cfe->iftype = reg & PCCARD_TPCE_IF_IFTYPE;
+ }
+ reg = pccard_tuple_read_1(tuple, idx);
+ idx++;
+
+ power = reg & PCCARD_TPCE_FS_POWER_MASK;
+ timing = reg & PCCARD_TPCE_FS_TIMING;
+ iospace = reg & PCCARD_TPCE_FS_IOSPACE;
+ irq = reg & PCCARD_TPCE_FS_IRQ;
+ memspace = reg & PCCARD_TPCE_FS_MEMSPACE_MASK;
+ misc = reg & PCCARD_TPCE_FS_MISC;
+
+ if (power) {
+ /* skip over power, don't save */
+ /* for each parameter selection byte */
+ for (i = 0; i < power; i++) {
+ reg = pccard_tuple_read_1(tuple, idx);
+ idx++;
+ /* for each bit */
+ for (j = 0; j < 7; j++) {
+ /* if the bit is set */
+ if ((reg >> j) & 0x01) {
+ /* skip over bytes */
+ do {
+ reg2 = pccard_tuple_read_1(tuple, idx);
+ idx++;
+ /*
+ * until
+ * non-extensi
+ * on byte
+ */
+ } while (reg2 & 0x80);
+ }
+ }
+ }
+ }
+ if (timing) {
+ /* skip over timing, don't save */
+ reg = pccard_tuple_read_1(tuple, idx);
+ idx++;
+
+ if ((reg & PCCARD_TPCE_TD_RESERVED_MASK) !=
+ PCCARD_TPCE_TD_RESERVED_MASK)
+ idx++;
+ if ((reg & PCCARD_TPCE_TD_RDYBSY_MASK) !=
+ PCCARD_TPCE_TD_RDYBSY_MASK)
+ idx++;
+ if ((reg & PCCARD_TPCE_TD_WAIT_MASK) !=
+ PCCARD_TPCE_TD_WAIT_MASK)
+ idx++;
+ }
+ if (iospace) {
+ if (tuple->length <= idx) {
+ DPRINTF(("ran out of space before TCPE_IO\n"));
+ goto abort_cfe;
+ }
+
+ reg = pccard_tuple_read_1(tuple, idx);
+ idx++;
+
+ if (reg & PCCARD_TPCE_IO_BUSWIDTH_8BIT)
+ cfe->flags |= PCCARD_CFE_IO8;
+ if (reg & PCCARD_TPCE_IO_BUSWIDTH_16BIT)
+ cfe->flags |= PCCARD_CFE_IO16;
+ cfe->iomask =
+ reg & PCCARD_TPCE_IO_IOADDRLINES_MASK;
+
+ if (reg & PCCARD_TPCE_IO_HASRANGE) {
+ reg = pccard_tuple_read_1(tuple, idx);
+ idx++;
+
+ cfe->num_iospace = 1 + (reg &
+ PCCARD_TPCE_IO_RANGE_COUNT);
+
+ if (cfe->num_iospace >
+ (sizeof(cfe->iospace) /
+ sizeof(cfe->iospace[0]))) {
+ DPRINTF(("too many io "
+ "spaces %d",
+ cfe->num_iospace));
+ state->card->error++;
+ break;
+ }
+ for (i = 0; i < cfe->num_iospace; i++) {
+ switch (reg & PCCARD_TPCE_IO_RANGE_ADDRSIZE_MASK) {
+ case PCCARD_TPCE_IO_RANGE_ADDRSIZE_ONE:
+ cfe->iospace[i].start =
+ pccard_tuple_read_1(tuple, idx);
+ idx++;
+ break;
+ case PCCARD_TPCE_IO_RANGE_ADDRSIZE_TWO:
+ cfe->iospace[i].start =
+ pccard_tuple_read_2(tuple, idx);
+ idx += 2;
+ break;
+ case PCCARD_TPCE_IO_RANGE_ADDRSIZE_FOUR:
+ cfe->iospace[i].start =
+ pccard_tuple_read_4(tuple, idx);
+ idx += 4;
+ break;
+ }
+ switch (reg &
+ PCCARD_TPCE_IO_RANGE_LENGTHSIZE_MASK) {
+ case PCCARD_TPCE_IO_RANGE_LENGTHSIZE_ONE:
+ cfe->iospace[i].length =
+ pccard_tuple_read_1(tuple, idx);
+ idx++;
+ break;
+ case PCCARD_TPCE_IO_RANGE_LENGTHSIZE_TWO:
+ cfe->iospace[i].length =
+ pccard_tuple_read_2(tuple, idx);
+ idx += 2;
+ break;
+ case PCCARD_TPCE_IO_RANGE_LENGTHSIZE_FOUR:
+ cfe->iospace[i].length =
+ pccard_tuple_read_4(tuple, idx);
+ idx += 4;
+ break;
+ }
+ cfe->iospace[i].length++;
+ }
+ } else {
+ cfe->num_iospace = 1;
+ cfe->iospace[0].start = 0;
+ cfe->iospace[0].length =
+ (1 << cfe->iomask);
+ }
+ }
+ if (irq) {
+ if (tuple->length <= idx) {
+ DPRINTF(("ran out of space before TCPE_IR\n"));
+ goto abort_cfe;
+ }
+
+ reg = pccard_tuple_read_1(tuple, idx);
+ idx++;
+
+ if (reg & PCCARD_TPCE_IR_SHARE)
+ cfe->flags |= PCCARD_CFE_IRQSHARE;
+ if (reg & PCCARD_TPCE_IR_PULSE)
+ cfe->flags |= PCCARD_CFE_IRQPULSE;
+ if (reg & PCCARD_TPCE_IR_LEVEL)
+ cfe->flags |= PCCARD_CFE_IRQLEVEL;
+
+ if (reg & PCCARD_TPCE_IR_HASMASK) {
+ /*
+ * it's legal to ignore the
+ * special-interrupt bits, so I will
+ */
+
+ cfe->irqmask =
+ pccard_tuple_read_2(tuple, idx);
+ idx += 2;
+ } else {
+ cfe->irqmask =
+ (1 << (reg & PCCARD_TPCE_IR_IRQ));
+ }
+ }
+ if (memspace) {
+ if (tuple->length <= idx) {
+ DPRINTF(("ran out of space before TCPE_MS\n"));
+ goto abort_cfe;
+ }
+
+ if (memspace == PCCARD_TPCE_FS_MEMSPACE_NONE) {
+ cfe->num_memspace = 0;
+ } else if (memspace == PCCARD_TPCE_FS_MEMSPACE_LENGTH) {
+ cfe->num_memspace = 1;
+ cfe->memspace[0].length = 256 *
+ pccard_tuple_read_2(tuple, idx);
+ idx += 2;
+ cfe->memspace[0].cardaddr = 0;
+ cfe->memspace[0].hostaddr = 0;
+ } else if (memspace ==
+ PCCARD_TPCE_FS_MEMSPACE_LENGTHADDR) {
+ cfe->num_memspace = 1;
+ cfe->memspace[0].length = 256 *
+ pccard_tuple_read_2(tuple, idx);
+ idx += 2;
+ cfe->memspace[0].cardaddr = 256 *
+ pccard_tuple_read_2(tuple, idx);
+ idx += 2;
+ cfe->memspace[0].hostaddr = cfe->memspace[0].cardaddr;
+ } else {
+ int lengthsize;
+ int cardaddrsize;
+ int hostaddrsize;
+
+ reg = pccard_tuple_read_1(tuple, idx);
+ idx++;
+
+ cfe->num_memspace = reg &
+ PCCARD_TPCE_MS_COUNT;
+
+ if (cfe->num_memspace >
+ (sizeof(cfe->memspace) /
+ sizeof(cfe->memspace[0]))) {
+ DPRINTF(("too many mem "
+ "spaces %d",
+ cfe->num_memspace));
+ state->card->error++;
+ break;
+ }
+ lengthsize =
+ ((reg & PCCARD_TPCE_MS_LENGTH_SIZE_MASK) >>
+ PCCARD_TPCE_MS_LENGTH_SIZE_SHIFT);
+ cardaddrsize =
+ ((reg & PCCARD_TPCE_MS_CARDADDR_SIZE_MASK) >>
+ PCCARD_TPCE_MS_CARDADDR_SIZE_SHIFT);
+ hostaddrsize =
+ (reg & PCCARD_TPCE_MS_HOSTADDR) ? cardaddrsize : 0;
+
+ if (lengthsize == 0) {
+ DPRINTF(("cfe memspace "
+ "lengthsize == 0"));
+ state->card->error++;
+ }
+ for (i = 0; i < cfe->num_memspace; i++) {
+ if (lengthsize) {
+ cfe->memspace[i].length =
+ 256 * pccard_tuple_read_n(tuple, lengthsize,
+ idx);
+ idx += lengthsize;
+ } else {
+ cfe->memspace[i].length = 0;
+ }
+ if (cfe->memspace[i].length == 0) {
+ DPRINTF(("cfe->memspace[%d].length == 0",
+ i));
+ state->card->error++;
+ }
+ if (cardaddrsize) {
+ cfe->memspace[i].cardaddr =
+ 256 * pccard_tuple_read_n(tuple, cardaddrsize,
+ idx);
+ idx += cardaddrsize;
+ } else {
+ cfe->memspace[i].cardaddr = 0;
+ }
+ if (hostaddrsize) {
+ cfe->memspace[i].hostaddr =
+ 256 * pccard_tuple_read_n(tuple, hostaddrsize,
+ idx);
+ idx += hostaddrsize;
+ } else {
+ cfe->memspace[i].hostaddr = 0;
+ }
+ }
+ }
+ }
+ if (misc) {
+ if (tuple->length <= idx) {
+ DPRINTF(("ran out of space before TCPE_MI\n"));
+ goto abort_cfe;
+ }
+
+ reg = pccard_tuple_read_1(tuple, idx);
+ idx++;
+
+ if (reg & PCCARD_TPCE_MI_PWRDOWN)
+ cfe->flags = PCCARD_CFE_POWERDOWN;
+ if (reg & PCCARD_TPCE_MI_READONLY)
+ cfe->flags = PCCARD_CFE_READONLY;
+ if (reg & PCCARD_TPCE_MI_AUDIO)
+ cfe->flags = PCCARD_CFE_AUDIO;
+ cfe->maxtwins = reg & PCCARD_TPCE_MI_MAXTWINS;
+
+ while (reg & PCCARD_TPCE_MI_EXT) {
+ reg = pccard_tuple_read_1(tuple, idx);
+ idx++;
+ }
+ }
+ /* skip all the subtuples */
+ }
+
+ abort_cfe:
+ DPRINTF(("CISTPL_CFTABLE_ENTRY\n"));
+ break;
+ default:
+ DPRINTF(("unhandled CISTPL %x\n", tuple->code));
+ break;
+ }
+
+ return (0);
+}
OpenPOWER on IntegriCloud