summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/pcmcia/pcmcia.c1429
-rw-r--r--sys/dev/pcmcia/pcmcia.h132
-rw-r--r--sys/dev/pcmcia/pcmcia_conf.c477
-rw-r--r--sys/dev/pcmcia/pcmcia_ioctl.h59
-rw-r--r--sys/dev/pcmcia/pcmciabus.h262
5 files changed, 2359 insertions, 0 deletions
diff --git a/sys/dev/pcmcia/pcmcia.c b/sys/dev/pcmcia/pcmcia.c
new file mode 100644
index 00000000000..baae3fecf92
--- /dev/null
+++ b/sys/dev/pcmcia/pcmcia.c
@@ -0,0 +1,1429 @@
+/*
+ * Copyright (c) 1994 Stefan Grefen. 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 Charles Hannum.
+ * This product includes software developed by Stefan Grefen.
+ * 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.
+ *
+ * $Id: pcmcia.c,v 1.1 1996/01/15 00:05:03 hvozda Exp $
+ */
+
+/* derived from scsiconf.c writte by Julian Elischer et al */
+/* TODO add modload support and loadable lists of devices */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+
+#include <dev/pcmcia/pcmcia.h>
+#include <dev/pcmcia/pcmciabus.h>
+#include <dev/pcmcia/pcmcia_ioctl.h>
+
+#ifdef IBM_WD
+#define PCMCIA_DEBUG
+#endif
+#ifdef PCMCIA_DEBUG
+# define PPRINTF(a) printf a
+#else
+# define PPRINTF(a)
+#endif
+
+static void pcmciadumpcf __P((struct pcmcia_conf *));
+static int pcmcia_strcmp __P((char *, char *, int, char *));
+
+void pcmcia_shuthook __P((void *));
+
+static struct pcmcia_adapter pcmcia_drivers[4];
+static int pcmcia_cntrl = 0;
+static int probed = 0;
+static struct device **deldevs = NULL;
+static int ndeldevs = 0;
+
+/* I've decided to re-ifdef these. It makes making a kernel easier
+ until I either get config(8) modified to deal somehow
+ or figure out a better to way declare the prototypes and
+ build up the knowndevs struct. Stefan may have ideas...
+*/
+
+#ifdef PCMCIA_ED
+extern struct pcmciadevs pcmcia_ed_devs[];
+#endif
+#ifdef PCMCIA_COM
+extern struct pcmciadevs pcmcia_com_devs[];
+#endif
+#ifdef PCMCIA_EP
+extern struct pcmciadevs pcmcia_ep_devs[];
+#endif
+
+static struct pcmciadevs *knowndevs[] = {
+#ifdef PCMCIA_ED
+ pcmcia_ed_devs,
+#endif
+#ifdef PCMCIA_COM
+ pcmcia_com_devs,
+#endif
+#ifdef PCMCIA_EP
+ pcmcia_ep_devs,
+#endif
+ NULL
+};
+
+#ifdef notyet
+static struct pcmciadevs *knowndevs[10] = { NULL };
+#define KNOWNSIZE (sizeof(knowndevs) / sizeof(knowndevs[0]))
+#endif
+
+#define PCMCIA_SERVICE(a,b,c,d,e) ((a)->chip_link->pcmcia_service(b,c,\
+ (void *) d,e))
+#define PCMCIA_MAP_IO(a,b,c,d,e) ((a)->chip_link->pcmcia_map_io(b,c,d,e))
+#define PCMCIA_MAP_INTR(a,b,c,d) ((a)->chip_link->pcmcia_map_intr(b,c,d))
+#define PCMCIA_MAP_MEM(a,b,c,d,e,f) ((a)->chip_link->pcmcia_map_mem(b,c,d,e,f))
+
+#define PCMCIA_BUS_INIT(a,b,c,d,e,f)((a)->bus_link->bus_init((b),(c),(d),(e)\
+ ,(f)))
+#define PCMCIA_BUS_SEARCH(a,b,c,d) ((a)->bus_link->bus_search((b),(c),(d)))
+#define PCMCIA_BUS_PROBE(a,b,c,d,e) ((a)->bus_link->bus_probe((b),(c),(d),(e)))
+#define PCMCIA_BUS_CONFIG(a,b,c,d,e)((a)->bus_link->bus_config((b),(c),(d),(e)))
+#define PCMCIA_BUS_UNCONFIG(a,b) ((a)->bus_link->bus_unconfig((b)))
+
+#define SCRATCH_MEM(a) ((a)->scratch_mem)
+#define SCRATCH_SIZE(a) ((a)->scratch_memsiz)
+#define SCRATCH_INUSE(a)((a)->scratch_inuse)
+
+/*
+ * Declarations
+ */
+struct pcmciadevs *pcmcia_probedev __P((struct pcmcia_link *));
+struct pcmciadevs *pcmcia_selectdev __P((char *, char *, char *, char *));
+int pcmcia_probe_bus __P((struct pcmcia_link *, int, int,
+ struct pcmcia_conf *));
+int pcmciabusmatch __P((struct device *, void *, void *));
+void pcmciabusattach __P((struct device *, struct device *, void *));
+
+struct cfdriver pcmciabuscd = {
+ NULL, "pcmcia", pcmciabusmatch, pcmciabusattach, DV_DULL,
+ sizeof(struct pcmciabus_softc), 1
+};
+
+#ifdef notyet
+int
+pcmcia_add_device(devs)
+ struct pcmciadevs *devs;
+{
+ int i;
+
+ if (devs == NULL)
+ return 0;
+
+ for (i = 0; i < KNOWNSIZE; i++)
+ if (knowndevs[i] == NULL)
+ break;
+
+ if (i == KNOWNSIZE)
+ panic("Too many pcmcia devices");
+
+ knowndevs[i] = devs;
+ for (; devs->devname != NULL; devs++)
+ printf("added %s\n", devs->devname);
+ return i;
+}
+#endif
+
+int
+pcmcia_register(adapter_softc, bus_link, chip_link, slot)
+ void *adapter_softc;
+ struct pcmciabus_link *bus_link;
+ struct pcmcia_funcs *chip_link;
+ int slot;
+{
+ PPRINTF(("- pcmcia_register\n"));
+ if (pcmcia_cntrl == 0)
+ bzero(pcmcia_drivers, sizeof(pcmcia_drivers));
+
+ if (pcmcia_cntrl < 4) {
+ pcmcia_drivers[slot].adapter_softc = adapter_softc;
+ pcmcia_drivers[slot].chip_link = chip_link;
+ pcmcia_drivers[slot].bus_link = bus_link;
+ pcmcia_cntrl++;
+ return 1;
+ }
+ return 0;
+}
+
+int
+pcmciabusmatch(parent, self, aux)
+ struct device *parent;
+ void *self;
+ void *aux;
+{
+ struct pcmciabus_softc *sc = (void *)self;
+ struct cfdata *cf = sc->sc_dev.dv_cfdata;
+ int i, found = 0;
+
+ PPRINTF(("- pcmciabusmatch\n"));
+ if (pcmcia_cntrl <= 0)
+ return 0;
+
+ for (i = 0; i < 4; i++)
+ if (pcmcia_drivers[i].bus_link) {
+ if (PCMCIA_BUS_INIT(&pcmcia_drivers[i], parent, cf,
+ aux, &pcmcia_drivers[i], 0))
+ found++;
+ }
+ return found != 0;
+}
+
+/*
+ * The routine called by the adapter boards to get all their
+ * devices configured in.
+ */
+void
+pcmciabusattach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct pcmciabus_softc *sc = (struct pcmciabus_softc *) self;
+ struct cfdata *cf = self->dv_cfdata;
+ int i, found = 0;
+
+ PPRINTF(("- pcmciabusattach\n"));
+ for (i = 0; i < 4; i++)
+ if (pcmcia_drivers[i].bus_link) {
+ if (PCMCIA_BUS_INIT(&pcmcia_drivers[i], parent, cf,
+ aux, &pcmcia_drivers[i], 1))
+ found++;
+ }
+
+ printf("\n");
+
+ pcmcia_probe_bus(NULL, sc->sc_dev.dv_unit, -1, NULL);
+}
+
+/*
+ * Probe the requested pcmcia bus. It must be already set up.
+ * -1 requests all set up pcmcia busses.
+ */
+int
+pcmcia_probe_busses(bus, slot)
+ int bus, slot;
+{
+ PPRINTF(("- pcmcia_probe_busses\n"));
+ if (bus == -1) {
+ for (bus = 0; bus < pcmciabuscd.cd_ndevs; bus++)
+ if (pcmciabuscd.cd_devs[bus])
+ pcmcia_probe_bus(NULL, bus, slot, NULL);
+ return 0;
+ } else {
+ return pcmcia_probe_bus(NULL, bus, slot, NULL);
+ }
+}
+
+/*
+ * Probe the requested pcmcia bus. It must be already set up.
+ */
+int
+pcmcia_probe_bus(link, bus, slot, cf)
+ struct pcmcia_link *link;
+ int bus, slot;
+ struct pcmcia_conf *cf;
+{
+ struct pcmciabus_softc *pcmcia;
+ int maxslot, minslot, maxlun, minlun;
+ struct pcmciadevs *bestmatch = NULL;
+ int spec_probe = (link != NULL);
+
+ PPRINTF(("- pcmcia_probe_bus\n"));
+ if (bus < 0 || bus >= pcmciabuscd.cd_ndevs)
+ return ENXIO;
+ pcmcia = pcmciabuscd.cd_devs[bus];
+ if (!pcmcia)
+ return ENXIO;
+
+ if (slot == -1) {
+ maxslot = 7;
+ minslot = 0;
+ } else {
+ if (slot < 0 || slot > 7)
+ return EINVAL;
+ maxslot = minslot = slot;
+ }
+
+ for (slot = minslot; slot <= maxslot; slot++) {
+ if (link = pcmcia->sc_link[slot]) {
+ if (link->devp)
+ continue;
+ }
+ if (pcmcia_drivers[slot >> 1].adapter_softc == NULL)
+ continue;
+
+ /*
+ * If we presently don't have a link block
+ * then allocate one
+ */
+ if (!link) {
+ pcmcia->sc_link[slot] = link =
+ malloc(sizeof(*link), M_TEMP, M_NOWAIT);
+ if (link == NULL)
+ return ENOMEM;
+ bzero(link, sizeof(*link));
+ link->opennings = 1;
+ link->adapter = &pcmcia_drivers[slot >> 1];
+ link->slot = slot;
+ }
+ bestmatch = pcmcia_probedev(link);
+ /*
+ * We already know what the device is. We use a
+ * special matching routine which insists that the
+ * cfdata is of the right type rather than putting
+ * more intelligence in individual match routines for
+ * each high-level driver.
+ * We must have the final probe do all of the comparisons,
+ * or we could get stuck in an infinite loop trying the same
+ * device repeatedly. We use the `fordriver' field of
+ * the pcmcia_link for now, rather than inventing a new
+ * structure just for the config_search().
+ */
+ if (link->fordriver == NULL) {
+ if (bestmatch)
+ link->fordriver = bestmatch->devname;
+ else {
+ if (!spec_probe) {
+ link->device = NULL;
+ link->devp = NULL;
+ PCMCIA_SERVICE(link->adapter,
+ link, PCMCIA_OP_POWER,
+ 0, 0);
+ }
+ }
+ }
+
+ if (spec_probe) {
+ if (cf && pcmcia_mapcard(link, -1, cf) != 0)
+ link->fordriver = NULL;
+ }
+
+ if (link->fordriver != NULL) {
+ int i;
+ struct device **delp = deldevs;
+ int found = 0;
+ link->device = bestmatch;
+ link->flags = (link->flags &
+ ~(PCMCIA_ATTACH_TYPE)) |
+ PCMCIA_REATTACH;
+ for (i = 0; i < ndeldevs; i++, delp++) {
+ if (*delp &&
+ pcmcia_configure((*delp)->dv_parent, *delp,
+ link)) {
+ link->flags = (link->flags &
+ ~PCMCIA_ATTACH_TYPE)
+ | PCMCIA_SLOT_INUSE;
+ found = 1;
+ *delp = NULL;
+ break;
+ }
+ }
+ if (!found) {
+ link->flags = (link->flags &
+ ~PCMCIA_ATTACH_TYPE) | PCMCIA_ATTACH;
+ if (PCMCIA_BUS_SEARCH(link->adapter,
+ &pcmcia->sc_dev,
+ link, NULL)) {
+ link->flags = (link->flags &
+ ~PCMCIA_ATTACH_TYPE)
+ | PCMCIA_SLOT_INUSE;
+ } else {
+ link->flags &= ~(PCMCIA_ATTACH_TYPE |
+ PCMCIA_SLOT_INUSE);
+ link->device = NULL;
+ printf(
+ "No matching config entry %s.\n",
+ link->fordriver ?
+ link->fordriver : "(NULL)");
+ if (!spec_probe)
+ PCMCIA_SERVICE(link->adapter,
+ link,
+ PCMCIA_OP_POWER,
+ 0, 0);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * given a target ask the device what
+ * it is, and find the correct driver table
+ * entry.
+ */
+struct pcmciadevs *
+pcmcia_probedev(link)
+ struct pcmcia_link *link;
+{
+ struct pcmcia_adapter *pca = link->adapter;
+ u_char scratch[CIS_MAXSIZE];
+ char manu[MAX_CIS_NAMELEN];
+ char model[MAX_CIS_NAMELEN];
+ char add_inf1[MAX_CIS_NAMELEN];
+ char add_inf2[MAX_CIS_NAMELEN];
+ int card_stat;
+ int err;
+ int pow = 0;
+ int slot = link->slot;
+
+ PPRINTF(("- pcmcia_probe_dev\n"));
+
+ printf("%s slot %d:",
+ ((struct device *) link->adapter->adapter_softc)->dv_xname,
+ slot & 1);
+
+ if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_STATUS,
+ &card_stat, 0)) != 0) {
+ printf("failed to get status %d\n", err);
+ return NULL;
+ }
+
+ if ((card_stat & PCMCIA_CARD_PRESENT) == 0) {
+ printf(" <slot empty>\n");
+ return NULL;
+ }
+
+ if (!(card_stat & PCMCIA_POWER)) {
+ pow = 1;
+ if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_POWER, 10000,
+ PCMCIA_POWER_ON|
+ PCMCIA_POWER_5V)) != 0) {
+ printf("failed to turn on power %d\n", err);
+ return NULL;
+ }
+ }
+
+ if (!(link->flags & (PCMCIA_SLOT_INUSE | CARD_IS_MAPPED))) {
+ if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_RESET,
+ 500000, 0)) != 0) {
+ printf("failed to reset %d\n", err);
+ PCMCIA_SERVICE(pca, link, PCMCIA_OP_POWER, 0, 0);
+ return NULL;
+ }
+ }
+
+ /*
+ * Ask the device what it is
+ */
+ if ((err = pcmcia_read_cis(link, scratch, 0, sizeof(scratch))) != 0) {
+ printf("failed to read cis info %d\n", err);
+ goto bad;
+ }
+
+ if ((err = pcmcia_get_cisver1(link, scratch, sizeof(scratch),
+ manu, model, add_inf1,
+ add_inf2)) != 0) {
+ printf("failed to get cis info %d\n", err);
+ goto bad;
+ }
+
+ printf(" <%s, %s", manu, model);
+ if (add_inf1[0])
+ printf(", %s", add_inf1);
+ if (add_inf2[0])
+ printf(", %s", add_inf2);
+ printf(">\n");
+
+
+ /*
+ * Try make as good a match as possible with
+ * available sub drivers
+ */
+ return pcmcia_selectdev(manu, model, add_inf1, add_inf2);
+bad:
+ if (!pow)
+ PCMCIA_SERVICE(pca, link, PCMCIA_OP_POWER, 0, 0);
+ return NULL;
+}
+
+/*
+ * Try make as good a match as possible with
+ * available sub drivers
+ */
+struct pcmciadevs *
+pcmcia_selectdev(manu, model, add_inf1, add_inf2)
+ char *manu, *model, *add_inf1, *add_inf2;
+{
+ u_int bestmatches = 0;
+ struct pcmciadevs *bestmatch = (struct pcmciadevs *) 0;
+ struct pcmciadevs **dlist, *dentry;
+
+ PPRINTF(("- pcmcia_selectdev\n"));
+ for (dlist = knowndevs; *dlist; dlist++)
+ for (dentry = *dlist; dentry &&
+ dentry->devname != NULL; dentry++) {
+ int match = 0;
+
+#ifdef PCMCIA_DEBUG
+ dentry->flags |= PC_SHOWME;
+#endif
+ match|=pcmcia_strcmp(dentry->manufacturer,
+ manu,dentry->flags,"manufacturer")<<6;
+ match|=pcmcia_strcmp(dentry->model,
+ model,dentry->flags,"model")<<4;
+ match|=pcmcia_strcmp(dentry->add_inf1,
+ add_inf1,dentry->flags,"info1")<<2;
+ match|=pcmcia_strcmp(dentry->add_inf2,
+ add_inf2,dentry->flags,"info2");
+/* the following was replaced by the wildcard function called above */
+#if 0
+ if (dentry->flags & PC_SHOWME)
+ printf("manufacturer = `%s'-`%s'\n",
+ dentry->manufacturer ?
+ dentry->manufacturer :
+ "X",
+ manu);
+ if (dentry->manufacturer) {
+ if (strcmp(dentry->manufacturer, manu) == 0) {
+ match |= 8;
+ } else {
+ continue;
+ }
+ }
+
+ if (dentry->flags & PC_SHOWME)
+ printf("model = `%s'-`%s'\n",
+ dentry->model ? dentry->model :
+ "X",
+ model);
+ if (dentry->model) {
+ if (strcmp(dentry->model, model) == 0) {
+ match |= 4;
+ } else {
+ continue;
+ }
+ }
+
+
+ if (dentry->flags & PC_SHOWME)
+ printf("info1 = `%s'-`%s'\n",
+ dentry->add_inf1 ? dentry->add_inf1 :
+ "X",
+ add_inf1);
+ if (dentry->add_inf1) {
+ if (strcmp(dentry->add_inf1, add_inf1) == 0) {
+ match |= 2;
+ } else {
+ continue;
+ }
+ }
+
+ if (dentry->flags & PC_SHOWME)
+ printf("info2 = `%s'-`%s'\n",
+ dentry->add_inf2 ? dentry->add_inf2 :
+ "X",
+ add_inf2);
+ if (dentry->add_inf2) {
+ if (strcmp(dentry->add_inf2, add_inf2) == 0) {
+ match |= 1;
+ } else {
+ continue;
+ }
+ }
+#endif
+#ifdef PCMCIA_DEBUG
+ printf("match == %d [%d]\n",match,bestmatches);
+#endif
+
+ if(match > bestmatches) {
+ bestmatches = match;
+ bestmatch = dentry;
+ }
+ }
+
+ return bestmatch;
+}
+
+int
+pcmcia_configure(parent, self, aux)
+ struct device *parent;
+ void *self;
+ void *aux;
+{
+ struct device *dev = self;
+ struct pcmcia_link *link = aux;
+ struct cfdata *cf = dev->dv_cfdata;
+ struct cfdriver *cd = cf->cf_driver;
+ char *devname = (char *) link->fordriver;
+ struct pcmciadevs *pcs = link->device;
+ struct pcmcia_device *pcd;
+ struct pcmcia_adapter *pca = link->adapter;
+ struct pcmcia_conf pc_cf;
+ u_char scratch[CIS_MAXSIZE];
+ int mymap = 0;
+
+ PPRINTF(("- pcmcia_configure\n"));
+
+ if (strcmp(devname, cd->cd_name) || !pca)
+ return 0;
+
+ if (pcs == NULL)
+ pcd = NULL;
+ else
+ pcd = pcs->dev;
+
+ PPRINTF(("pcmcia_configure: %x\n", pcd));
+ if (!(link->flags & CARD_IS_MAPPED)) {
+ /* read 'suggested' configuration */
+ PPRINTF(("pcmcia_configure: calling read cis\n"));
+ if (pcmcia_read_cis(link, scratch, 0, sizeof(scratch)) != 0)
+ return 0;
+
+ bzero(&pc_cf, sizeof(pc_cf));
+
+ PPRINTF(("pcmcia_configure: calling get cf\n"));
+ if (pcmcia_get_cf(link, scratch, sizeof(scratch), -1,
+ &pc_cf) != 0)
+ return 0;
+#ifdef PCMCIA_DEBUG
+ pcmciadumpcf(&pc_cf);
+#endif
+ /* and modify it (device specific) */
+ if (pcd && pcd->pcmcia_config) {
+ PPRINTF(("pcmcia_configure: calling config\n"));
+ if (pcd->pcmcia_config(link, dev, &pc_cf, cf))
+ return 0;
+
+ if ((pc_cf.cfgtype & CFGENTRYMASK) == CFGENTRYID) {
+ PPRINTF(("pcmcia_configure: calling cf2\n"));
+ if (pcmcia_get_cf(link, scratch,
+ sizeof(scratch), -2,
+ &pc_cf) != 0)
+ return 0;
+
+ PPRINTF(("pcmcia_configure: calling conf2\n"));
+ if (pcd->pcmcia_config(link, dev, &pc_cf, cf))
+ return 0;
+ /* give it a try */
+ if(pc_cf.cfgid==0)
+ pc_cf.cfgid=1;
+ }
+ } else {
+ PPRINTF(("pcmcia_configure: calling bus config\n"));
+ if (PCMCIA_BUS_CONFIG(pca, link, dev, &pc_cf, cf))
+ return 0;
+ }
+#ifdef PCMCIA_DEBUG
+ pcmciadumpcf(&pc_cf);
+#endif
+
+ if (pcmcia_mapcard(link, -1, &pc_cf) != 0)
+ return 0;
+
+ mymap = 1;
+ }
+ link->devp = dev;
+
+ PPRINTF(("pcmcia_configure: calling bus probe\n"));
+ if (!(PCMCIA_BUS_PROBE(pca, parent, dev, cf, link))) {
+ PPRINTF(("pcmcia_configure: bus probe failed\n"));
+ goto bad;
+ }
+
+ if (pcd && pcd->pcmcia_insert && pcd->pcmcia_insert(link, dev, cf)) {
+ PPRINTF(("pcmcia_configure: pcmcia_insert failed\n"));
+ goto bad;
+ }
+
+ link->shuthook = shutdownhook_establish(pcmcia_shuthook,
+ (void *)link);
+ return 1;
+
+bad:
+ link->devp = NULL;
+ if (mymap)
+ pcmcia_unmapcard(link);
+ printf("pcmcia_configure: configuration error\n");
+ return 0;
+}
+
+void
+pcmcia_shuthook(arg)
+void *arg;
+{
+ struct pcmcia_link *link = (struct pcmcia_link *)arg;
+ if (pcmcia_unconfigure(link) == 0) {
+ /*
+ * turn off power too.
+ */
+ PCMCIA_SERVICE(link->adapter, link, PCMCIA_OP_RESET, 500000, 0);
+ PCMCIA_SERVICE(link->adapter, link, PCMCIA_OP_POWER, 0, 0);
+ }
+}
+
+int
+pcmcia_unconfigure(link)
+ struct pcmcia_link *link;
+{
+ int status;
+ int i, err;
+ struct device **delp;
+ struct device *dev;
+ struct pcmcia_adapter *pca = link->adapter;
+ struct pcmcia_device *pcd;
+
+ PPRINTF(("- pcmcia_unconfigure\n"));
+ if (link->devp == NULL)
+ return ENODEV;
+
+ if (link->device)
+ pcd = link->device->dev;
+ else
+ pcd = NULL;
+
+ if (link->flags & CARD_IS_MAPPED) {
+ if (pcd && pcd->pcmcia_remove) {
+ if (pcd->pcmcia_remove(link, link->devp))
+ return EBUSY;
+ }
+ else {
+ if (PCMCIA_BUS_UNCONFIG(pca, link))
+ return EBUSY;
+ }
+ if (pcmcia_unmapcard(link) != 0)
+ return EBUSY;
+ }
+ delp = deldevs;
+ for (i = 0; delp && *delp && i < ndeldevs; i++, delp++)
+ continue;
+ if (i >= ndeldevs) {
+ int sz = ndeldevs ? (ndeldevs * 2) :
+ (MINALLOCSIZE / sizeof(void *));
+ struct device **ndel = malloc(sz * sizeof(void *),
+ M_DEVBUF, M_NOWAIT);
+ if (!ndel) {
+ PPRINTF(("pcmcia_delete: creating dev array"));
+ return ENOMEM;
+ }
+ bzero(ndel, sz * sizeof(void *));
+ if (ndeldevs) {
+ bcopy(deldevs, ndel, ndeldevs * sizeof(void *));
+ free(deldevs, M_DEVBUF);
+ }
+ ndeldevs = sz - 1;
+ deldevs = ndel;
+ delp = deldevs + i;
+ }
+ dev = *delp = link->devp;
+ link->devp = NULL;
+ printf("device %s in pcmcia slot %d detached\n", dev->dv_xname,
+ link->slot);
+ shutdownhook_disestablish(link->shuthook);
+ link->shuthook = 0;
+ return 0;
+}
+
+int
+pcmcia_mapcard(link, unit, pc_cf)
+ struct pcmcia_link *link;
+ struct pcmcia_conf *pc_cf;
+{
+ struct pcmcia_adapter *pca = link->adapter;
+ int s, i, err;
+ PPRINTF(("- pcmcia_mapcard\n"));
+
+ if (pca == NULL)
+ return ENXIO;
+ s = splbio();
+
+ while (SCRATCH_INUSE(pca))
+ sleep((caddr_t) & SCRATCH_INUSE(pca), PZERO - 1);
+
+ SCRATCH_INUSE(pca) = 1;
+ splx(s);
+ for (i = 0; i < pc_cf->memwin; i++) {
+ if ((err = PCMCIA_MAP_MEM(pca, link,
+ (caddr_t) pc_cf->mem[i].start,
+ pc_cf->mem[i].caddr,
+ pc_cf->mem[i].len,
+ (pc_cf->mem[i].flags &
+ (PCMCIA_MAP_16 | PCMCIA_MAP_ATTR)) |
+ i | PCMCIA_PHYSICAL_ADDR)) != 0) {
+ PPRINTF(("pcmcia_mapcard: mapmem %d err%d\n", i, err));
+ goto error;
+ }
+ }
+ for (i = 0; i < pc_cf->iowin; i++) {
+ if ((err = PCMCIA_MAP_IO(pca, link, pc_cf->io[i].start,
+ pc_cf->io[i].len,
+ (pc_cf->io[i].flags & (PCMCIA_MAP_16 |
+ PCMCIA_MAP_8)) | i)) != 0) {
+ PPRINTF(("pcmcia_mapcard: mapio %d err %d\n", i, err));
+ goto error;
+ }
+ }
+
+ if ((pc_cf->irq_num & 0xf) > 0) {
+ if ((err = PCMCIA_MAP_INTR(pca, link, pc_cf->irq_num & 0xf,
+ 0)) != 0) {
+ PPRINTF(("pcmcia_mapcard: map_intr %d err %d\n",
+ pc_cf->irq_num & 0xf, err));
+ goto error;
+ }
+ }
+ /* Now we've mapped everything enable it */
+ if ((err = PCMCIA_MAP_MEM(pca, link, SCRATCH_MEM(pca),
+ pc_cf->cfg_off & (~(SCRATCH_SIZE(pca) - 1)), SCRATCH_SIZE(pca),
+ PCMCIA_MAP_ATTR | PCMCIA_LAST_WIN)) != 0) {
+ PPRINTF(("pcmcia_mapcard: enable err %d\n", err));
+ goto error;
+ }
+
+ if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_RESET, -500000,
+ pc_cf->iocard)) != 0) {
+ PPRINTF(("failed to reset %d\n", err));
+ goto error;
+ }
+
+#define GETMEM(x) SCRATCH_MEM(pca)[(pc_cf->cfg_off & \
+ (SCRATCH_SIZE(pca) - 1)) + x]
+ if ((pc_cf->cfgtype & DOSRESET)) {
+ GETMEM(0) = PCMCIA_SRESET;
+ delay(50000);
+ }
+
+
+ PPRINTF(("CMDR %x\n",((pc_cf->cfgtype & CFGENTRYID) ?
+ pc_cf->cfgid |CFGENTRYID:
+ (pc_cf->cfgtype & CFGENTRYMASK)|1)|
+ (pc_cf->irq_level ? PCMCIA_LVLREQ : 0)
+ ));
+
+ GETMEM(0) = ((pc_cf->cfgtype & CFGENTRYID) ?
+ pc_cf->cfgid |CFGENTRYID:
+ (pc_cf->cfgtype & CFGENTRYMASK)|1)|
+ (pc_cf->irq_level ? PCMCIA_LVLREQ : 0);
+ delay(50000);
+
+ if (pc_cf->cfg_regmask & (1 << (PCMCIA_SCR / 2)))
+ GETMEM(PCMCIA_SCR) = (link->slot & 1) | 0x10;
+
+#if 0
+ DPRINTF(("CCSR %x\n", GETMEM(PCMCIA_CCSR]));
+ if (GETMEM(PCMCIA_CCSR] & PCMCIA_POWER_DOWN) {
+ GETMEM(PCMCIA_CCSR] &= ~PCMCIA_POWER_DOWN;
+ DPRINTF(("CCSR now %x\n", GETMEM(PCMCIA_CCSR]));
+ }
+#endif
+
+ if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_WAIT,
+ 500000, 0)) != 0)
+ PPRINTF(("failed to initialize %d\n", err));
+error:
+ PCMCIA_MAP_MEM(pca, link, 0, 0, 0, PCMCIA_LAST_WIN | PCMCIA_UNMAP);
+ if (err != 0) {
+ for (i = 0; i < pc_cf->memwin; i++) {
+ PCMCIA_MAP_MEM(pca, link,
+ (caddr_t) pc_cf->mem[i].start,
+ pc_cf->mem[i].caddr,
+ pc_cf->mem[i].len,
+ (pc_cf->mem[i].flags & (PCMCIA_MAP_16 |
+ PCMCIA_MAP_ATTR)) | i |
+ PCMCIA_PHYSICAL_ADDR | PCMCIA_UNMAP);
+ }
+ for (i = 0; i < pc_cf->iowin; i++) {
+ PCMCIA_MAP_IO(pca, link, pc_cf->io[i].start,
+ pc_cf->io[i].len,
+ (pc_cf->io[i].flags & (PCMCIA_MAP_16 |
+ PCMCIA_MAP_8)) | i | PCMCIA_UNMAP);
+ }
+ PCMCIA_MAP_INTR(pca, link, pc_cf->irq_num, PCMCIA_UNMAP);
+ link->flags &= ~CARD_IS_MAPPED;
+ link->iowin = 0;
+ link->memwin = 0;
+ link->intr = 0;
+ } else {
+ link->flags |= CARD_IS_MAPPED;
+ link->iowin = pc_cf->iowin;
+ link->memwin = pc_cf->memwin;
+ link->intr = pc_cf->irq_num;
+ }
+ s = splbio();
+ SCRATCH_INUSE(pca) = 0;
+ wakeup((caddr_t) & SCRATCH_INUSE(pca));
+ splx(s);
+ return err;
+}
+
+int
+pcmcia_unmapcard(link)
+ struct pcmcia_link *link;
+{
+ int i;
+ struct pcmcia_adapter *pca = link->adapter;
+ PPRINTF(("- pcmcia_unmapcard\n"));
+ if (!pca)
+ return ENODEV;
+
+ for (i = 0; i < link->memwin; i++)
+ PCMCIA_MAP_MEM(pca, link, 0, 0, 0, (i | PCMCIA_UNMAP));
+
+ for (i = 0; i < link->iowin; i++)
+ PCMCIA_MAP_IO(pca, link, 0, 0, (i | PCMCIA_UNMAP));
+
+ PCMCIA_MAP_INTR(pca, link, link->intr, PCMCIA_UNMAP);
+ PCMCIA_SERVICE(pca, link, PCMCIA_OP_RESET, 0, 0);
+ link->flags &= ~(CARD_IS_MAPPED | PCMCIA_SLOT_INUSE);
+ link->iowin = 0;
+ link->memwin = 0;
+ link->intr = 0;
+ return 0;
+}
+
+
+static int
+pcmcia_mapcard_and_configure(link, unit, pc_cf)
+ struct pcmcia_link *link;
+ struct pcmcia_conf *pc_cf;
+ int unit;
+{
+ int err;
+ int mymap = 0;
+
+ PPRINTF(("- pcmcia_mapcard_and_configure\n"));
+ if (pc_cf->driver_name[0][0]) {
+#if 0
+ if ((err = pcmcia_mapcard(link, unit, pc_cf)) != 0) {
+ return err;
+ }
+ mymap=1;
+#endif
+ link->fordriver = pc_cf->driver_name[0];
+ } else {
+ link->fordriver = NULL;
+ pc_cf = NULL;
+ }
+ pcmcia_probe_bus(link, 0, link->slot, pc_cf);
+ if ((link->flags & PCMCIA_SLOT_INUSE) == 0) {
+ if (mymap)
+ pcmcia_unmapcard(link);
+ return ENODEV;
+ }
+ return 0;
+}
+
+
+int
+pcmcia_read_cis(link, scratch, offs, len)
+ struct pcmcia_link *link;
+ u_char *scratch;
+ int offs, len;
+{
+ struct pcmcia_adapter *pca = link->adapter;
+ int s;
+ int err = 0;
+ int j = 0;
+ u_char *p = SCRATCH_MEM(pca);
+ int size = SCRATCH_SIZE(pca);
+ volatile int *inuse = &SCRATCH_INUSE(pca);
+
+ PPRINTF(("- pcmcia_read_cis\n"));
+ if (pca == NULL)
+ return ENXIO;
+
+ s = splbio();
+ while (*inuse)
+ sleep((caddr_t) inuse, PZERO - 1);
+ *inuse = 1;
+ splx(s);
+
+ while (len > 0) {
+ int pgoff = offs / size;
+ int toff = offs - (pgoff * size);
+ int tlen = min(len + toff, size / 2) - toff;
+ int i;
+
+ if ((err = PCMCIA_MAP_MEM(pca, link, p, pgoff, size,
+ PCMCIA_MAP_ATTR |
+ PCMCIA_LAST_WIN)) != 0)
+ goto error;
+
+ for (i = 0; i < tlen; j++, i++)
+ scratch[j] = p[toff + i * 2];
+
+ PCMCIA_MAP_MEM(pca, link, p, 0, size,
+ PCMCIA_LAST_WIN | PCMCIA_UNMAP);
+ len -= tlen;
+ }
+error:
+ s = splbio();
+ *inuse = 0;
+ wakeup((caddr_t) inuse);
+ splx(s);
+
+ return err;
+}
+
+/* here we start our pseudodev for controlling the slots */
+#define PCMCIABUS_UNIT(a) (minor(a))
+#define PCMCIABUS_SLOT(a) (a&0x7)
+#define PCMCIABUS_CHIPIID(a) (a&0x3)
+#define PCMCIABUS_CHIP 0x10
+#define PCMCIABUS_BUS 0x20
+#define PCMCIABUS_DEVTYPE(a) ((a)&(PCMCIABUS_CHIP|PCMCIABUS_BUS))
+static int busopen = 0;
+static int chipopen[4] = {0, 0, 0, 0};
+
+int
+pcmciabusopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ int unit = PCMCIABUS_UNIT(dev);
+ int chipid, slot;
+ struct pcmcia_link *link;
+ struct pcmciabus_softc *pcmcia;
+
+ PPRINTF(("- pcmciabusopen\n"));
+ if (pcmcia_cntrl == 0)
+ return ENXIO;
+ switch (PCMCIABUS_DEVTYPE(unit)) {
+ case PCMCIABUS_BUS:
+ if (unit != PCMCIABUS_BUS)
+ return ENXIO;
+ if (busopen)
+ return EBUSY;
+ busopen = 1;
+ break;
+
+ case PCMCIABUS_CHIP:
+ chipid = PCMCIABUS_CHIPIID(unit);
+ if (chipid > 3)
+ return ENXIO;
+ if (pcmcia_drivers[chipid].adapter_softc == NULL)
+ return ENXIO;
+
+ if (chipopen[chipid])
+ return EBUSY;
+
+ chipopen[chipid] = 1;
+ break;
+
+ case 0:
+ slot = PCMCIABUS_SLOT(unit);
+ chipid = slot >> 1;
+
+ if (chipid > 7)
+ return ENXIO;
+
+ if (pcmcia_drivers[chipid].adapter_softc == NULL)
+ return ENXIO;
+ pcmcia = pcmciabuscd.cd_devs[0];
+ link = pcmcia->sc_link[slot];
+
+ if (link->flags & PCMCIA_SLOT_OPEN)
+ return EBUSY;
+
+ link->flags |= PCMCIA_SLOT_OPEN;
+ break;
+
+ default:
+ return ENXIO;
+
+ }
+ return 0;
+}
+
+
+int
+pcmciabusclose(dev)
+{
+ int unit = PCMCIABUS_UNIT(dev);
+ int chipid, slot;
+ struct pcmcia_link *link;
+ struct pcmciabus_softc *pcmcia;
+ int s;
+
+ PPRINTF(("- pcmciabusclose\n"));
+ if (pcmcia_cntrl == 0)
+ return ENXIO;
+ switch (PCMCIABUS_DEVTYPE(unit)) {
+ case PCMCIABUS_BUS:
+ busopen = 0;
+ break;
+
+ case PCMCIABUS_CHIP:
+ chipid = PCMCIABUS_CHIPIID(unit);
+ chipopen[chipid] = 0;
+ break;
+
+ case 0:
+ slot = PCMCIABUS_SLOT(unit);
+ pcmcia = pcmciabuscd.cd_devs[0];
+ link = pcmcia->sc_link[slot];
+
+ s = splclock();
+ link->flags &= ~(PCMCIA_SLOT_OPEN|PCMCIA_SLOT_EVENT);
+ splx(s);
+ break;
+
+ default:
+ return ENXIO;
+ }
+ return 0;
+}
+
+int
+pcmciachip_ioctl(chipid, cmd, data)
+ int chipid, cmd;
+ caddr_t data;
+{
+ int err = 0;
+ struct pcmcia_adapter *pca = &pcmcia_drivers[chipid];
+ struct pcmcia_link link;
+ struct pcmcia_regs *pi = (void *) data;
+
+ PPRINTF(("- pcmciachip_ioctl\n"));
+ if (pca->chip_link == NULL || pca->adapter_softc == NULL)
+ return ENXIO;
+
+ switch (cmd) {
+ case PCMCIAIO_READ_REGS:
+ pi->chip = chipid;
+ link.adapter = pca;
+ link.slot = chipid << 1;
+ return PCMCIA_SERVICE(pca, &link, PCMCIA_OP_GETREGS,
+ pi->chip_data, 0);
+ }
+ return ENOTTY;
+}
+
+int
+pcmciaslot_ioctl(link, slotid, cmd, data)
+ struct pcmcia_link *link;
+ int slotid, cmd;
+ caddr_t data;
+{
+ int err = 0;
+ struct pcmcia_adapter *pca = &pcmcia_drivers[slotid >> 1];
+
+ PPRINTF(("- pcmciaslot_ioctl\n"));
+ if (link == NULL || pca->chip_link == NULL ||
+ pca->adapter_softc == NULL)
+ return ENXIO;
+
+ switch (cmd) {
+ case PCMCIAIO_GET_STATUS:
+ {
+ struct pcmcia_status *pi = (void *) data;
+ pi->slot = slotid;
+ pi->status = 0;
+ err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_STATUS,
+ &pi->status, 0);
+ if (!err) {
+ pi->status |= ((link->flags & CARD_IS_MAPPED) ?
+ PCMCIA_CARD_IS_MAPPED : 0) |
+ ((link->flags & PCMCIA_SLOT_INUSE) ?
+ PCMCIA_CARD_INUSE : 0);
+ }
+ return err;
+ }
+
+ case PCMCIAIO_GET_INFO:
+ {
+ struct pcmcia_info *pi = (void *) data;
+ int status;
+
+ if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_STATUS,
+ &status, 0)) != 0)
+ return err;
+ if ((status & PCMCIA_CARD_PRESENT) == 0)
+ return ENODEV;
+ pi->slot = slotid;
+ return pcmcia_read_cis(link, pi->cis_data, 0,
+ CIS_MAXSIZE);
+ }
+
+ case PCMCIAIO_CONFIGURE:
+ {
+ struct pcmcia_conf *pc_cf = (void *) data;
+ return pcmcia_mapcard_and_configure(link, -1, pc_cf);
+ }
+
+ case PCMCIAIO_UNCONFIGURE:
+ return pcmcia_unconfigure(link);
+
+ case PCMCIAIO_UNMAP:
+ return pcmcia_unmapcard(link);
+
+ case PCMCIAIO_SET_POWER:
+ {
+ int pi = *(int *) data;
+ pi &= 0x3;
+ switch (pi) {
+ case PCMCIASIO_POWER_OFF:
+ return PCMCIA_SERVICE(pca, link,
+ PCMCIA_OP_POWER, 0, 0);
+
+ case PCMCIASIO_POWER_5V:
+ case PCMCIASIO_POWER_3V:
+ case PCMCIASIO_POWER_AUTO:
+ err = PCMCIA_SERVICE(pca, link,
+ PCMCIA_OP_POWER,
+ 10000, pi);
+ if (err)
+ return err;
+
+ err = PCMCIA_SERVICE(pca, link,
+ PCMCIA_OP_RESET,
+ 500000, 0);
+ if (err) {
+ PPRINTF(("failed to reset %d\n", err));
+ PCMCIA_SERVICE(pca, link,
+ PCMCIA_OP_POWER, 0, 0);
+ return err;
+ }
+ return 0;
+
+ default:
+ return EINVAL;
+ }
+ }
+
+ case PCMCIAIO_READ_COR:
+ {
+ struct pcmcia_info *pi = (void *)data;
+ struct pcmcia_conf pc_cf;
+ int status,s;
+
+ err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_STATUS,
+ &status, 0);
+ if (err)
+ return err;
+ if ((status & PCMCIA_CARD_PRESENT) == 0)
+ return ENODEV;
+
+ if (status = pcmcia_read_cis(link, pi->cis_data, 0,
+ CIS_MAXSIZE))
+ return status;
+
+ bzero(&pc_cf, sizeof(pc_cf));
+ if (pcmcia_get_cf(link, pi->cis_data,
+ sizeof(pi->cis_data), -1,
+ &pc_cf) != 0 )
+ return EIO;
+
+ s=splbio();
+
+ while(SCRATCH_INUSE(pca))
+ sleep((caddr_t)&SCRATCH_INUSE(pca), PZERO - 1);
+
+ SCRATCH_INUSE(pca) = 1;
+ splx(s);
+ if ((err = PCMCIA_MAP_MEM(pca, link, SCRATCH_MEM(pca),
+ pc_cf.cfg_off &
+ ~(SCRATCH_SIZE(pca)-1),
+ SCRATCH_SIZE(pca),
+ PCMCIA_MAP_ATTR|
+ PCMCIA_LAST_WIN)) == 0) {
+ int m, i;
+ u_char *d = pi->cis_data,*p;
+ p = SCRATCH_MEM(pca)+
+ (pc_cf.cfg_off & (SCRATCH_SIZE(pca)-1));
+ for (i = 0, m = 1; i < 32; i++, m <<= 1) {
+ if (pc_cf.cfg_regmask & m) {
+ *d++ = i;
+ *d++ = p[i*2];
+ }
+ }
+ *d++ = 0xff;
+ *d++ = 0xff;
+ PCMCIA_MAP_MEM(pca, link, SCRATCH_MEM(pca),
+ 0,SCRATCH_SIZE(pca),
+ PCMCIA_LAST_WIN|PCMCIA_UNMAP);
+ }
+ s = splbio();
+ SCRATCH_INUSE(pca)=0;
+ wakeup((caddr_t)&SCRATCH_INUSE(pca));
+ splx(s);
+ return err;
+ }
+ default:
+ return ENOTTY;
+ }
+ return ENOTTY;
+}
+
+int
+pcmciabusioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ int unit = PCMCIABUS_UNIT(dev);
+ struct pcmciabus_softc *pcmcia;
+ struct pcmcia_link *link;
+
+ PPRINTF(("- pcmciabus_ioctl\n"));
+ pcmcia = pcmciabuscd.cd_devs[0];
+ if (pcmcia_cntrl == 0 || pcmcia == NULL)
+ return ENXIO;
+ switch (PCMCIABUS_DEVTYPE(unit)) {
+#if 0
+ case PCMCIABUS_BUS:
+ return pcmciabus_ioctl(0, cmd, data);
+#endif
+ case PCMCIABUS_CHIP:
+ return pcmciachip_ioctl(PCMCIABUS_CHIPIID(unit), cmd, data);
+ case 0:
+ link = pcmcia->sc_link[PCMCIABUS_SLOT(unit)];
+ return pcmciaslot_ioctl(link, PCMCIABUS_SLOT(unit), cmd, data);
+ default:
+ return ENXIO;
+ }
+}
+
+int
+pcmciabusselect(device, rw, p)
+ dev_t device;
+ int rw;
+ struct proc *p;
+{
+ int s;
+ int unit = PCMCIABUS_UNIT(device);
+ struct pcmciabus_softc *pcmcia;
+ struct pcmcia_link *link;
+
+ PPRINTF(("- pcmciabus_ioctl\n"));
+ pcmcia = pcmciabuscd.cd_devs[0];
+
+ switch (PCMCIABUS_DEVTYPE(unit)) {
+ case 0:
+ link = pcmcia->sc_link[PCMCIABUS_SLOT(unit)];
+ break;
+ case PCMCIABUS_BUS:
+ case PCMCIABUS_CHIP:
+ default:
+ return 0;
+ }
+
+ s = splclock(); /* XXX something higher than all devices that can plug in.... */
+ switch (rw) {
+ case FREAD:
+ case FWRITE:
+ break;
+ case 0:
+ if (link->flags & PCMCIA_SLOT_EVENT) {
+ link->flags &= ~PCMCIA_SLOT_EVENT;
+ splx(s);
+ return 1;
+ }
+ selrecord(p, &link->pcmcialink_sel);
+ break;
+ }
+ splx(s);
+ return 0;
+}
+
+int
+pcmciabusmmap()
+{
+ return ENXIO;
+}
+
+/* pcmcia template string match. A '*' matches any number of characters.
+ A NULL template matches all strings.
+ return-value
+ 0 nomatch
+ 1 wildcard match
+ 2 excact match
+ */
+static int
+pcmcia_strcmp(templ,val,flags,msg)
+ char *templ;
+ char *val;
+ int flags;
+ char *msg;
+{
+ char *ltempl=NULL,*lval=NULL;
+
+ if (flags & PC_SHOWME)
+ printf("%s = `%s'-`%s'\n", msg, templ ? templ : "X", val);
+
+ if(templ==NULL)
+ return 1;
+ while(*val) {
+ while(*templ=='*') {
+ ltempl=++templ;
+ lval=val;
+ }
+ if(*templ==*val) {
+ templ++;
+ val++;
+ } else {
+ if(ltempl==NULL)
+ return 0;
+ val=++lval;
+ templ=ltempl;
+ }
+ }
+ if(*templ!=0 && *templ!='*')
+ return 0;
+ return ltempl?1:2;
+}
+
+#ifdef PCMCIA_DEBUG
+static void
+pcmciadumpcf(cf)
+ struct pcmcia_conf * cf;
+{
+ int i;
+ static char *ios[] = {
+ "auto", "8bit", "16bit", "illegal"
+ };
+ printf("Driver name %s\n", cf->driver_name[0]);
+ printf("CFG offset %x\n", cf->cfg_off);
+ printf("IRQ type %s%s\n", cf->irq_level ? "Level " : "",
+ cf->irq_pulse ? "Pulse" : "");
+ printf("IRQ num %x\n", cf->irq_num);
+ printf("CFG type %x %x\n", cf->cfgtype,cf->cfgid);
+ printf("Cardtype %s\n", cf->iocard ? "IO" : "MEM");
+ for (i = 0; i < cf->iowin; i++) {
+ printf("iowin %x-%x %s\n", cf->io[i].start,
+ cf->io[i].start + cf->io[i].len - 1,
+ ios[(cf->io[i].flags &
+ (PCMCIA_MAP_8 | PCMCIA_MAP_16)) >> 8]);
+ }
+ for (i = 0; i < cf->memwin; i++) {
+ printf("memwin (%x)%x-%x %x\n",
+ cf->mem[i].caddr,
+ cf->mem[i].start,
+ cf->mem[i].start + cf->mem[i].len - 1,
+ cf->mem[i].flags);
+ }
+}
+#endif
diff --git a/sys/dev/pcmcia/pcmcia.h b/sys/dev/pcmcia/pcmcia.h
new file mode 100644
index 00000000000..67df7217ff5
--- /dev/null
+++ b/sys/dev/pcmcia/pcmcia.h
@@ -0,0 +1,132 @@
+#ifndef __PCMCIA_H__
+#define __PCMCIA_H__
+
+#define MAX_CIS_NAMELEN /*32*/64 /* version info string len */
+
+/*
+ * Configuration Registers
+ *
+ * These are the registers required by Release 2.0 of the standard
+ * (Section 4.15)
+ */
+
+/* Offsets for register ordering */
+#define PCMCIA_COR 0x00 /* Configuration and Option Register */
+#define PCMCIA_CCSR 0x02 /* Card Configuration and Status Register */
+#define PCMCIA_PIR 0x04 /* Pin Replacement Register */
+#define PCMCIA_SCR 0x06 /* Socket and Copy Register */
+
+/* Now register bits, ordered by reg # */
+
+/* For Configuration and Option Register (PCMCIA_COR) */
+/*#define PCMCIA_MEMIO 0x01 /* Use I/O Space */
+/*#define PCMCIA_CNFG 0x0e /* I/O decoding configuration */
+#define PCMCIA_CNFGMASK 0x3f /* Use template */
+#define PCMCIA_LVLREQ 0x40 /* Generate level mode interrupts */
+#define PCMCIA_SRESET 0x80 /* Reset Card */
+
+/* For Card Configuration and Status Register (PCMCIA_CCSR) */
+#define PCMCIA_INTR 0x02 /* Interrupt Pending */
+#define PCMCIA_POWER_DOWN 0x04
+#define PCMCIA_AUDIO_ENA 0x08
+#define PCMCIA_IOIS8 0x20
+#define PCMCIA_SIGCHG_ENA 0x40
+#define PCMCIA_CHANGED 0x80
+
+/* Pin Replacement Register (PCMCIA_PIR) */
+#define PCMCIA_WP_STATUS 0x01
+#define PCMCIA_READY_STATUS 0x02
+#define PCMCIA_BVD2_STATUS 0x04
+#define PCMCIA_BVD1_STATUS 0x08
+#define PCMCIA_WP_EVENT 0x10
+#define PCMCIA_READY_EVENT 0x20
+#define PCMCIA_BVD2_EVENT 0x40
+#define PCMCIA_BVD1_EVENT 0x80
+
+
+/* For Socket and Copy Register (PCMCIA_SCR) */
+#define PCMCIA_SOCKNUM 0x0f /* Which socket I'm sitting in */
+#define PCMCIA_COPNUM 0x70 /* Which instance I am. */
+
+/*
+ * CIS Tuple defines
+ */
+#define CIS_MAXSIZE 512
+
+/* Define tuple types */
+#define CIS_NULL 0x00 /* null tuple */
+#define CIS_DEVICE 0x01 /* Device descriptor, common mem */
+#define CIS_DEVICE_A 0x17 /* Device descriptor, attribute mem */
+#define CIS_DEVICE_TYPE 0xf0 /* type mask */
+#define CIS_DEVICE_TYPE_SHIFT 4 /* type offset */
+#define CIS_DEVICE_WPS 0x08 /* WPS mask */
+#define CIS_DEVICE_SPEED 0x07 /* speed mask */
+#define CIS_DEVICE_ADDRS 0xf8 /* # addr units */
+#define CIS_DEVICE_ADDRS_SHIFT 3 /* # addr units offset */
+#define CIS_DEVICE_SIZE 0x07
+#define CIS_CSUM 0x10 /* Checksum field */
+#define CIS_NOLINK 0x14 /* No Link */
+#define CIS_VER1 0x15 /* Level 1 Version/Product info */
+#define CIS_CFG_INFO 0x1a /* Configuration info map */
+#define TPCC_RASZ 0x03 /* size of regaddr */
+#define TPCC_RASZ_SHIFT 0
+#define TPCC_RMSZ 0x3c /* size of regmask */
+#define TPCC_RMSZ_SHIFT 2
+#define TPCC_LAST 0x3f /* last con entry idx */
+#define TPCC_LAST_SHIFT 0
+#define CIS_CFG_ENT 0x1b /* Configuration info entry */
+#define TPCE_INDX_ENTRY 0x3f /* config entry # */
+#define TPCE_INDX_DEF 0x40 /* default bit */
+#define TPCE_INDX_INT 0x80 /* interface bit */
+#define TPCE_IF_TYPE 0x0f /* interface type */
+#define TPCE_IF_BVD 0x10 /* BVD active bit */
+#define TPCE_IF_WP 0x20 /* WP active bit */
+#define TPCE_IF_RDYBSY 0x40 /* RdyBsy active bit */
+#define TPCE_IF_MWAIT 0x80 /* Wait Sig req. bit */
+#define TPCE_FS_PWR 0x03 /* Power */
+#define TPCE_FS_PWR_VCC 0x01 /* Vcc struct */
+#define TPCE_FS_PWR_VPP 0x02 /* Vpp struct */
+#define TPCE_FS_TD 0x04 /* Timing */
+#define TPCE_FS_TD_WAIT 0x03 /* wait scale */
+#define TPCE_FS_TD_RDY 0x1c /* rdy/bsy scale */
+#define TPCE_FS_TD_RDY_SHIFT 2
+#define TPCE_FS_TD_RSV 0xe0 /* reserved scale */
+#define TPCE_FS_TD_RSV_SHIFT 5
+#define TPCE_FS_IO 0x08 /* I/O Space */
+#define TPCE_FS_IO_LINES 0x1f /* IO addr lines */
+#define TPCE_FS_IO_BUS8 0x20 /* bus 8 bit */
+#define TPCE_FS_IO_BUS16 0x40 /* bus 16 bit */
+#define TPCE_FS_IO_RANGE 0x80 /* range bit */
+#define TPCE_FS_IO_LEN 0xc0 /* block len size */
+#define TPCE_FS_IO_LEN_SHIFT 6
+#define TPCE_FS_IO_SIZE 0x30 /* block size size */
+#define TPCE_FS_IO_SIZE_SHIFT 4
+#define TPCE_FS_IO_NUM 0x0f /* # of blocks */
+#define TPCE_FS_IRQ 0x10 /* IRQ */
+#define TPCE_FS_IRQ_SHARE 0x80 /* int sharing */
+#define TPCE_FS_IRQ_PULSE 0x40 /* pulse request */
+#define TPCE_FS_IRQ_LEVEL 0x20 /* level-trig int */
+#define TPCE_FS_IRQ_MASK 0x10 /* irq mask bit */
+#define TPCE_FS_IRQ_IRQN 0x0f /* irqn mask */
+#define TPCE_FS_IRQ_VEND 0x08 /* vendor sig */
+#define TPCE_FS_IRQ_BERR 0x04 /* bus error */
+#define TPCE_FS_IRQ_IOCK 0x02 /* io check */
+#define TPCE_FS_IRQ_NMI 0x01 /* nmi */
+#define TPCE_FS_MEM 0x60 /* Mem Space */
+#define TPCE_FS_MEM_SHIFT 5
+#define TPCE_FS_MEM_HOST 0x80
+#define TPCE_FS_MEM_ADDR 0x60
+#define TPCE_FS_MEM_ADDR_SHIFT 5
+#define TPCE_FS_MEM_LEN 0x18
+#define TPCE_FS_MEM_LEN_SHIFT 3
+#define TPCE_FS_MEM_WINS 0x07
+#define TPCE_FS_MISC 0x80 /* Misc */
+#define CIS_MFG 0x20 /* Manufacturer's ID */
+#define CIS_FUNC 0x21 /* Function ID */
+#define CIS_FUNE 0x22 /* Function Extension */
+#define CIS_DRIVER 0x77 /* Driver ID */
+#define CIS_END 0xff /* Last Entry */
+
+extern int pcmcia_configure __P((struct device *, void *, void *));
+
+#endif /* __PCMCIA_H__ */
diff --git a/sys/dev/pcmcia/pcmcia_conf.c b/sys/dev/pcmcia/pcmcia_conf.c
new file mode 100644
index 00000000000..ab0e5b0d741
--- /dev/null
+++ b/sys/dev/pcmcia/pcmcia_conf.c
@@ -0,0 +1,477 @@
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/device.h>
+
+#include <dev/pcmcia/pcmcia.h>
+#include <dev/pcmcia/pcmciabus.h>
+#include <dev/pcmcia/pcmcia_ioctl.h>
+
+#ifdef CFG_DEBUG
+static
+void dump(addr, len)
+ u_char *addr;
+ int len;
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ printf("%02x ", addr[i]);
+ if (i != 0 && (i & 0xf) == 0)
+ printf("\n");
+ }
+ if (i != 0 && (i & 0xf) == 0)
+ printf("\n");
+}
+#endif
+
+int
+pcmcia_get_cf(pc_link, data, dlen, idx, pc_cf)
+ struct pcmcia_link *pc_link;
+ u_char *data;
+ int dlen, idx;
+ struct pcmcia_conf *pc_cf;
+{
+ u_char code, len, *tbuf, *endp;
+ int done;
+
+ endp = data + dlen;
+
+ done = 0;
+ while (!done && data < endp) {
+ code = *data++;
+ if (code == CIS_NULL) {
+ continue;
+ }
+ len = *data++;
+
+ tbuf = data;
+ data += len;
+ switch (code) {
+ case CIS_END:
+ done = 1;
+ break;
+ case CIS_CFG_INFO:
+ read_cfg_info(tbuf, len, pc_cf);
+ break;
+ case CIS_CFG_ENT:
+ if ((idx & CFGENTRYMASK) != CFGENTRYID ||
+ pc_cf->cfgid == 0)
+ parse_cfent(tbuf, len, idx, pc_cf);
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+
+int
+read_cfg_info(tbuf, len, pc_cf)
+ u_char *tbuf;
+ int len;
+ struct pcmcia_conf *pc_cf;
+{
+ int rasz, rmsz;
+
+ rasz = (tbuf[0] & TPCC_RASZ) >> TPCC_RASZ_SHIFT;
+ rmsz = (tbuf[0] & TPCC_RMSZ) >> TPCC_RMSZ_SHIFT;
+
+#ifdef CFG_DEBUG
+ printf("read_cfg_info\n");
+ dump(tbuf, len);
+#endif
+
+ pc_cf->cfg_off = 0;
+ switch (rasz) {
+ case 3:
+ pc_cf->cfg_off |= (tbuf[5] << 24);
+ case 2:
+ pc_cf->cfg_off |= (tbuf[4] << 16);
+ case 1:
+ pc_cf->cfg_off |= (tbuf[3] << 8);
+ case 0:
+ pc_cf->cfg_off |= tbuf[2];
+ }
+
+ tbuf += rasz + 3;
+ pc_cf->cfg_regmask = 0;
+ switch (rmsz & 3) {
+ case 3:
+ pc_cf->cfg_regmask |= (tbuf[3] << 24);
+ case 2:
+ pc_cf->cfg_regmask |= (tbuf[2] << 16);
+ case 1:
+ pc_cf->cfg_regmask |= (tbuf[1] << 8);
+ case 0:
+ pc_cf->cfg_regmask |= tbuf[0];
+ }
+}
+
+int
+parse_cfent(tbuf, len, slotid, pc_cf)
+ u_char *tbuf;
+ int len;
+ int slotid;
+ struct pcmcia_conf *pc_cf;
+{
+ int i, idx, defp, iop, io_16, ios, ftrs, intface, k;
+ int host_addr_p, addr_size, len_size;
+
+#ifdef CFG_DEBUG
+ printf("parse_cfent\n");
+ dump(tbuf, len);
+#endif
+
+ i = 0;
+ intface = (tbuf[i] & TPCE_INDX_INT);
+ idx = (tbuf[i] & TPCE_INDX_ENTRY);
+ defp = (tbuf[i] & TPCE_INDX_DEF);
+
+ if ((idx == slotid) || (defp && slotid!=-2 &&
+ (slotid & CFGENTRYMASK) == CFGENTRYMASK)) {
+ int j;
+ if (intface) {
+ i++;
+ pc_cf->iocard = (tbuf[i] & TPCE_IF_TYPE) == 1;
+ }
+ i++;
+ ftrs = tbuf[i++];
+ for (j = 0; j < (ftrs & TPCE_FS_PWR); j++) {
+ int pwr_desc = tbuf[i++];
+ /* for each struct, skip all parameter defns */
+ for (k = 0; k < 8; pwr_desc >>= 1, k++) {
+ if (pwr_desc & 0x01) {
+ /* skip bytes until non-ext found */
+ while (tbuf[i++] & 0x80)
+ continue;
+ }
+ }
+ }
+ /* TODO read timing info */
+ if (ftrs & TPCE_FS_TD) {
+#define BONE(a,b) (j & a) != 7 << b ? 1 : 0
+ int j = tbuf[i++];
+ i += ((j & TPCE_FS_TD_WAIT) != 3 ? 1 : 0);
+ i += BONE(TPCE_FS_TD_RDY,TPCE_FS_TD_RDY_SHIFT);
+ i += BONE(TPCE_FS_TD_RSV,TPCE_FS_TD_RSV_SHIFT);
+#undef BONE
+ }
+ if (ftrs & TPCE_FS_IO) {
+ int io_addrs[16], io_lens[16];
+ int io_16, io_block_len, io_block_size, io_lines;
+ int io_range;
+
+ iop = 1;
+ io_lines = tbuf[i] & TPCE_FS_IO_LINES;
+ io_16 = tbuf[i] & TPCE_FS_IO_BUS16;
+ io_range = tbuf[i] &TPCE_FS_IO_RANGE;
+ i++;
+ if (io_range) {
+ int iptr, ilen, elen;
+
+ io_block_len = (tbuf[i] & TPCE_FS_IO_LEN) >>
+ TPCE_FS_IO_LEN_SHIFT;
+ io_block_size = (tbuf[i] & TPCE_FS_IO_SIZE) >>
+ TPCE_FS_IO_SIZE_SHIFT;
+ ios = (tbuf[i] & TPCE_FS_IO_NUM) + 1;
+ i++;
+ if ((ftrs & TPCE_FS_IRQ) != 0) {
+ iptr=(ios * elen) + i;
+#define IRQTYPE (TPCE_FS_IRQ_PULSE|TPCE_FS_IRQ_LEVEL)
+#define IRQMASK TPCE_FS_IRQ_MASK
+ if ((tbuf[iptr] & IRQTYPE) == 0)
+ if ((tbuf[iptr-elen] &
+ IRQTYPE) != 0)
+ iptr -= elen;
+ if ((tbuf[iptr] & IRQMASK) != 0)
+ ilen = 2;
+ else
+ ilen=1;
+ }
+ else
+ ilen=0;
+
+ if ((i + (ios * elen) + ilen) > len) {
+ printf(
+"Warning: CIS range info doesn't fit in entry! Reducing # of ranges by 1\n");
+ ios--;
+ }
+
+ for (j = 0; j < ios; j++) {
+ io_addrs[j] = io_lens[j] = 0;
+ switch (io_block_size) {
+ case 3:
+ io_addrs[j] |= tbuf[i+3] << 24;
+ io_addrs[j] |= tbuf[i+2] << 16;
+ case 2:
+ io_addrs[j] |= tbuf[i+1] << 8;
+ case 1:
+ io_addrs[j] |= tbuf[i];
+ break;
+ }
+ pc_cf->io[j].start = io_addrs[j];
+ i += io_block_size +
+ (io_block_size == 3 ? 1 : 0);
+ switch (io_block_len) {
+ case 3:
+ io_lens[j] |= tbuf[i+3] << 24;
+ io_lens[j] |= tbuf[i+2] << 16;
+ case 2:
+ io_lens[j] |= tbuf[i+1] << 8;
+ case 1:
+ io_lens[j] |= tbuf[i];
+ break;
+ }
+ io_lens[j]++;
+ if(io_lens[j] & 1) {
+ printf(
+"Odd IO window length!! (Assuming incorrect CIS entry %d)\n", io_lens[j]);
+ io_lens[j]--;
+ }
+
+ pc_cf->io[j].len = io_lens[j];
+ pc_cf->io[j].flags = io_16 ?
+ PCMCIA_MAP_16 : PCMCIA_MAP_8;
+ i += io_block_len +
+ (io_block_len == 3 ? 1 : 0);
+ }
+ pc_cf->iowin = ios;
+ }
+ else {
+ pc_cf->iowin = 1;
+ pc_cf->io[0].len = 1 << io_lines;
+ pc_cf->io[0].start= 0 ;
+ pc_cf->io[0].flags = io_16 ?
+ PCMCIA_MAP_16 : PCMCIA_MAP_8;
+ }
+ }
+ if (ftrs & TPCE_FS_IRQ) {
+ int irq_mask, irqp, irq;
+ pc_cf->irq_level = (tbuf[i] & TPCE_FS_IRQ_LEVEL) != 0;
+ pc_cf->irq_pulse = (tbuf[i] & TPCE_FS_IRQ_PULSE) != 0;
+ pc_cf->irq_share = (tbuf[i] & TPCE_FS_IRQ_SHARE) != 0;
+ if (tbuf[i] & TPCE_FS_IRQ_MASK) {
+ pc_cf->irq_mask = (tbuf[i+2] << 8) + tbuf[i+1];
+ if (pc_cf->irq_mask & (1 << 2))
+ pc_cf->irq_mask |= 1 << 9;
+ i += 2;
+ } else {
+ pc_cf->irq_num = tbuf[i] & TPCE_FS_IRQ_IRQN;
+ pc_cf->irq_mask = -1;
+ }
+
+ i++;
+ }
+ if (ftrs & TPCE_FS_MEM) {
+ int memp, mems, mem_lens[16], mem_caddrs[16],
+ mem_haddrs[16];
+ memp = 1;
+ switch ((ftrs & TPCE_FS_MEM) >> TPCE_FS_MEM_SHIFT) {
+ case 1:
+ mems = 1;
+ mem_lens[0] = (tbuf[i+1] << 8) + tbuf[i];
+ mem_lens[0] <<= 8;
+
+ break;
+ case 2:
+ mems = 1;
+ mem_lens[0] = (tbuf[i+1] << 8) + tbuf[i];
+ mem_caddrs[0] = mem_haddrs[0] =
+ (tbuf[i+3] << 8) + tbuf[i+2];
+
+ mem_lens[0] <<= 8;
+ mem_caddrs[0] <<= 8;
+
+ break;
+ case 3:
+ host_addr_p = tbuf[i] & TPCE_FS_MEM_HOST;
+ addr_size = (tbuf[i] & TPCE_FS_MEM_ADDR) >>
+ TPCE_FS_MEM_ADDR_SHIFT;
+ len_size = (tbuf[i] & TPCE_FS_MEM_LEN) >>
+ TPCE_FS_MEM_LEN_SHIFT;
+ mems = (tbuf[i] & TPCE_FS_MEM_WINS) + 1;
+ i++;
+ for (j = 0; j < mems; j++) {
+ mem_lens[j] = 0;
+ mem_caddrs[j] = 0;
+ mem_haddrs[j] = 0;
+ switch (len_size) {
+ case 3:
+ mem_lens[j] |=
+ (tbuf[i+2] << 16);
+ case 2:
+ mem_lens[j] |=
+ (tbuf[i+1] << 8);
+ case 1:
+ mem_lens[j] |= tbuf[i];
+ }
+ i += len_size;
+ switch (addr_size) {
+ case 3:
+ mem_caddrs[j] |=
+ (tbuf[i+2] << 16);
+ case 2:
+ mem_caddrs[j] |=
+ (tbuf[i+1] << 8);
+ case 1:
+ mem_caddrs[j] |= tbuf[i];
+ }
+ i += addr_size;
+ if (host_addr_p) {
+ switch (addr_size) {
+ case 3:
+ mem_haddrs[j] |=
+ (tbuf[i+2] << 16);
+ case 2:
+ mem_haddrs[j] |=
+ (tbuf[i+1] << 8);
+ case 1:
+ mem_haddrs[j] |=
+ tbuf[i];
+ }
+ i += addr_size;
+ }
+ mem_lens[j] <<= 8;
+ mem_caddrs[j] <<= 8;
+ mem_haddrs[j] <<= 8;
+
+ }
+ }
+ for (j = 0; j < mems; j++) {
+ pc_cf->mem[j].len = mem_lens[j];
+ pc_cf->mem[j].caddr = mem_caddrs[j];
+ pc_cf->mem[j].start = mem_haddrs[j];
+ pc_cf->mem[j].flags = 0;
+ }
+ pc_cf->memwin = mems;
+ } else
+ pc_cf->memwin = 0;
+ return;
+ }
+
+
+ if (slotid == -2 ) {
+ /* find matching slotid */
+ struct pcmcia_conf tmp_cf;
+ /* get defaults */
+ parse_cfent(tbuf, len, -1, &tmp_cf);
+ /* change to selected */
+ parse_cfent(tbuf, len, idx, &tmp_cf);
+#ifdef CFG_DEBUG
+ printf("slotid %d %d iowin %d %d memwin %d %d wins %x %x %d %d\n",
+ pc_cf->iocard , tmp_cf.iocard,
+ pc_cf->iowin , tmp_cf.iowin,
+ pc_cf->memwin , tmp_cf.memwin,
+ pc_cf->io[0].start , tmp_cf.io[0].start,
+ pc_cf->io[0].len , tmp_cf.io[0].len
+ );
+#endif
+
+ if((pc_cf->iocard == tmp_cf.iocard) && /* same type */
+ (pc_cf->iowin == tmp_cf.iowin) &&
+ (pc_cf->memwin == tmp_cf.memwin)) {
+ int i;
+ for (i = 0; i < tmp_cf.iowin; i++)
+ if (pc_cf->io[i].len != tmp_cf.io[i].len ||
+ pc_cf->io[i].start != tmp_cf.io[i].start)
+ return;
+
+ for (i = 0; i < tmp_cf.memwin; i++)
+ if (pc_cf->mem[i].len!=tmp_cf.mem[i].len ||
+ pc_cf->mem[i].start!=tmp_cf.mem[i].start)
+ return;
+
+ /* *pc_cf = tmp_cf;/**/
+ pc_cf->cfgid = idx;
+ }
+ return;
+ }
+}
+
+void
+pcmcia_getstr(buf, pptr, end)
+ char *buf;
+ u_char **pptr;
+ u_char *end;
+{
+ u_char *ptr = *pptr;
+ char *eb = buf + MAX_CIS_NAMELEN - 1;
+
+ while (buf < eb && ptr < end)
+ switch (*ptr) {
+ case 0x00:
+ ptr++;
+ /*FALLTHROUGH*/
+ case 0xff:
+ *pptr = ptr;
+ *buf = '\0';
+ return;
+
+ default:
+ *buf++ = *ptr++;
+ break;
+ }
+ printf("Warning: Maximum CIS string length exceeded\n");
+ *buf = '\0';
+
+ /* Keep going until we find the end */
+ while (ptr < end)
+ switch (*ptr) {
+ case 0x00:
+ ptr++;
+ /*FALLTHROUGH*/
+ case 0xff:
+ *pptr = ptr;
+ return;
+
+ default:
+ ptr++;
+ break;
+ }
+
+ *pptr = ptr;
+}
+
+
+int
+pcmcia_get_cisver1(pc_link, data, len, manu, model, add_inf1, add_inf2)
+ struct pcmcia_link *pc_link;
+ u_char *data;
+ int len;
+ char *manu, *model, *add_inf1, *add_inf2;
+{
+ u_char *p, *end;
+
+ p = data;
+ end = data + len;
+ while ((*p != (u_char) 0xff) && (p < end)) {
+ int clen = *(p + 1);
+ int maj, min;
+ if (*p == CIS_VER1) {
+ u_char *pp = p + 2;
+ maj = *pp++;
+ min = *pp++;
+ if (maj != 4 || min != 1) {
+ printf("wrong version id %d.%d for card in slot %d\n",
+ maj, min, pc_link->slot);
+ return ENODEV;
+ }
+ pcmcia_getstr(manu, &pp, end);
+ pcmcia_getstr(model, &pp, end);
+ pcmcia_getstr(add_inf1, &pp, end);
+ pcmcia_getstr(add_inf2, &pp, end);
+ if (*pp != (u_char) 0xff) {
+ printf("WARNING: broken id for card in slot %d\n", pc_link->slot);
+ printf("manu %s model %s add_inf1 %s add_inf2 %s\n", manu, model, add_inf1, add_inf2);
+ return 0;
+ }
+ return 0;
+ }
+ p += clen + 2;
+ }
+ printf("%x %x\n", p, end);
+ return ENODEV;
+}
diff --git a/sys/dev/pcmcia/pcmcia_ioctl.h b/sys/dev/pcmcia/pcmcia_ioctl.h
new file mode 100644
index 00000000000..535e12241b7
--- /dev/null
+++ b/sys/dev/pcmcia/pcmcia_ioctl.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1993, 1994 Stefan Grefen. 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 dipclaimer.
+ * 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 Stefan Grefen.
+ * 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.
+ */
+struct pcmcia_info {
+ int slot;
+ u_char cis_data[CIS_MAXSIZE];
+};
+
+struct pcmcia_status {
+ int slot;
+ int status;
+};
+
+struct pcmcia_regs {
+ int chip;
+ int chiptype;
+#define PCMCIA_CHIP_UNKNOWN 0
+#define PCMCIA_PCIC 1
+ u_char chip_data[CIS_MAXSIZE];
+};
+
+#define PCMCIAIO_GET_STATUS _IOR('s', 128, struct pcmcia_status)
+#define PCMCIAIO_GET_INFO _IOR('s', 129, struct pcmcia_info)
+#define PCMCIAIO_SET_POWER _IOW('s', 139, int)
+#define PCMCIASIO_POWER_5V 0x3
+#define PCMCIASIO_POWER_3V 0x5
+#define PCMCIASIO_POWER_AUTO 0x7
+#define PCMCIASIO_POWER_OFF 0x0
+#define PCMCIAIO_CONFIGURE _IOW('s', 140, struct pcmcia_conf)
+#define PCMCIAIO_UNMAP _IOW('s', 141, int)
+#define PCMCIAIO_UNCONFIGURE _IOW('s', 142, int)
+#define PCMCIAIO_READ_COR _IOR('s', 143, struct pcmcia_info)
+#define PCMCIAIO_READ_REGS _IOWR('s', 160, struct pcmcia_regs)
+
diff --git a/sys/dev/pcmcia/pcmciabus.h b/sys/dev/pcmcia/pcmciabus.h
new file mode 100644
index 00000000000..4dceb987690
--- /dev/null
+++ b/sys/dev/pcmcia/pcmciabus.h
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 1993, 1994 Stefan Grefen. 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 dipclaimer.
+ * 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 Charles Hannum.
+ * 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.
+ *
+ * $Id: pcmciabus.h,v 1.1 1996/01/15 00:05:13 hvozda Exp $
+ */
+ /* derived from scsicconf.[ch] writenn by Julian Elischer et al */
+
+#ifndef _PCMCIA_PCMCIABUS_H_
+#define _PCMCIA_PCMCIABUS_H_ 1
+
+#include <sys/queue.h>
+#include <sys/select.h>
+#include <machine/cpu.h>
+
+/*
+ * The following documentation tries to describe the relationship between the
+ * various structures defined in this file:
+ *
+ * each adapter type has a pcmcia_adapter struct. This describes the adapter and
+ * identifies routines that can be called to use the adapter.
+ * each device type has a pcmcia_device struct. This describes the device and
+ * identifies routines that can be called to use the device.
+ * each existing device position (pcmciabus + port)
+ * can be described by a pcmcia_link struct.
+ * Only port positions that actually have devices, have a pcmcia_link
+ * structure assigned. so in effect each device has pcmcia_link struct.
+ * The pcmcia_link structure contains information identifying both the
+ * device driver and the adapter driver for that port on that pcmcia bus,
+ * and can be said to 'link' the two.
+ * each individual pcmcia bus has an array that points to all the pcmcia_link
+ * structs associated with that pcmcia bus. Slots with no device have
+ * a NULL pointer.
+ * each individual device also knows the address of it's own pcmcia_link
+ * structure.
+ *
+ * -------------
+ *
+ * The key to all this is the pcmcia_link structure which associates all the
+ * other structures with each other in the correct configuration. The
+ * pcmcia_link is the connecting information that allows each part of the
+ * pcmcia system to find the associated other parts.
+ */
+
+
+struct pcmcia_link;
+struct pcmcia_conf;
+struct pcmcia_adapter;
+
+/*
+ * These entrypoints are called by the high-end drivers to get services from
+ * whatever low-end drivers they are attached to each adapter type has one of
+ * these statically allocated.
+ */
+struct pcmcia_funcs {
+/* 4 map io range */
+ int (*pcmcia_map_io) __P((struct pcmcia_link *, u_int, u_int, int));
+/* 8 map memory window */
+ int (*pcmcia_map_mem) __P((struct pcmcia_link *, caddr_t,
+ u_int, u_int, int));
+/*12 map interrupt */
+ int (*pcmcia_map_intr) __P((struct pcmcia_link *, int, int));
+/*26 power on/off etc */
+ int (*pcmcia_service) __P((struct pcmcia_link *, int, void *, int));
+};
+
+struct pcmciabus_link { /* Link back to the bus we are on */
+ /* Bus specific configure */
+ int (*bus_config) __P((struct pcmcia_link *, struct device *,
+ struct pcmcia_conf *, struct cfdata *));
+ /* Bus specific unconfigure */
+ int (*bus_unconfig) __P((struct pcmcia_link *));
+ /* Bus specific probe */
+ int (*bus_probe) __P((struct device *, void *,
+ void *, struct pcmcia_link *));
+ /* Bus specific search */
+ int (*bus_search) __P((struct device *, void *, cfprint_t));
+ /* initialize scratch */
+ int (*bus_init) __P((struct device *, struct cfdata *,
+ void *, struct pcmcia_adapter *, int));
+};
+struct pcmcia_adapter {
+ struct pcmcia_funcs *chip_link;
+ struct pcmciabus_link *bus_link;
+ void * adapter_softc;
+ caddr_t scratch_mem; /* pointer to scratch window */
+ int scratch_memsiz; /* size of scratch window */
+ int scratch_inuse; /* window in use */
+};
+
+#define PCMCIA_MAP_ATTR 0x0100 /* for memory only */
+#define PCMCIA_MAP_8 0x0100 /* for io only */
+#define PCMCIA_MAP_16 0x0200
+#define PCMCIA_UNMAP 0x0400
+#define PCMCIA_PHYSICAL_ADDR 0x0800
+#define PCMCIA_UNMAP_ALL 0x0c00
+#define PCMCIA_FIXED_WIN 0x1000
+#define PCMCIA_LAST_WIN 0x0010
+#define PCMCIA_FIRST_WIN 0x0020
+#define PCMCIA_ANY_WIN 0x0030
+
+#define PCMCIA_OP_RESET 0x0000
+#define PCMCIA_OP_POWER 0x0001
+#define PCMCIA_OP_STATUS 0x0002
+#define PCMCIA_OP_GETREGS 0x0003
+#define PCMCIA_OP_WAIT 0x0004
+
+#define PCMCIA_POWER_ON 0x0001
+#define PCMCIA_POWER_5V 0x0002
+#define PCMCIA_POWER_3V 0x0004
+#define PCMCIA_POWER_AUTO 0x0008
+
+#define PCMCIA_CARD_PRESENT 0x0001
+#define PCMCIA_BATTERY 0x0002
+#define PCMCIA_WRITE_PROT 0x0004
+#define PCMCIA_READY 0x0008
+#define PCMCIA_POWER 0x0010
+#define PCMCIA_POWER_PP 0x0020
+#define PCMCIA_CARD_IS_MAPPED 0x1000
+#define PCMCIA_CARD_INUSE 0x2000
+
+
+/*
+ * This structure describes the connection between an adapter driver and
+ * a device driver, and is used by each to call services provided by
+ * the other, and to allow generic pcmcia glue code to call these services
+ * as well.
+ */
+struct pcmcia_link {
+ char pcmciabus; /* the Nth pcmciabus */
+ char slot; /* slot of this dev */
+ char flags;
+#define CARD_IS_MAPPED 0x01
+#define PCMCIA_ATTACH 0x02
+#define PCMCIA_REATTACH 0x04
+#define PCMCIA_SLOT_INUSE 0x08
+#define PCMCIA_ATTACH_TYPE (PCMCIA_ATTACH|PCMCIA_REATTACH)
+#define PCMCIA_SLOT_EVENT 0x80
+#define PCMCIA_SLOT_OPEN 0x40
+ char opennings;
+
+ char iowin;
+ char memwin;
+ char intr;
+ char dummy;
+ struct pcmcia_adapter *adapter; /* adapter entry points etc. */
+ struct pcmciadevs *device; /* device entry points etc. */
+ void *devp; /* pointer to configured device */
+ void *fordriver; /* for private use by the driver */
+ void *shuthook; /* shutdown hook handle */
+ struct selinfo pcmcialink_sel; /* for select users */
+};
+
+/*
+ * One of these is allocated and filled in for each pcmcia bus.
+ * it holds pointers to allow the pcmcia bus to get to the driver
+ * it also has a template entry which is the prototype struct
+ * supplied by the adapter driver, this is used to initialise
+ * the others, before they have the rest of the fields filled in
+ */
+struct pcmciabus_softc {
+ struct device sc_dev;
+ struct pcmcia_link *sc_link[8];
+};
+
+struct pcmcia_conf {
+ int irq_share:1;
+ int irq_level:1; /* 1 level */
+ int irq_pulse:1; /* 1 pulse */
+ int irq_vend:1;
+ int irq_iock:1;
+ int irq_berr:1;
+ int irq_nmi:1;
+ int iocard:1;
+ u_char iowin;
+ u_char memwin;
+ u_char irq_num;
+ u_char cfgtype;
+#define CFGENTRYID 0x20
+#define CFGENTRYMASK (CFGENTRYID|(CFGENTRYID-1))
+#define DOSRESET 0x40
+ int cfg_regmask;
+ int irq_mask;
+ int cfg_off;
+ struct iowin {
+ int start;
+ int len;
+ int flags;
+ }io[4];
+ struct memwin {
+ int start;
+ int caddr;
+ int len;
+ int flags;
+ }mem[4];
+ char driver_name[8][4]; /* up to four different functions on a card */
+ int unitid;
+ int cfgid;
+};
+
+struct pcmcia_device {
+ char *name;
+ int (*pcmcia_config) __P((struct pcmcia_link *, struct device *,
+ struct pcmcia_conf *, struct cfdata *));
+ int (*pcmcia_probe) __P((struct device *, void *,
+ void *, struct pcmcia_link *));
+ int (*pcmcia_insert) __P((struct pcmcia_link *, struct device *,
+ struct cfdata *));
+ int (*pcmcia_remove) __P((struct pcmcia_link *, struct device *));
+};
+
+struct pcmciadevs {
+ char *devname;
+ int flags; /* 1 show my comparisons during boot(debug) */
+#define PC_SHOWME 0x01
+ char *manufacturer;
+ char *model;
+ char *add_inf1;
+ char *add_inf2;
+ void *param;
+ struct pcmcia_device *dev;
+};
+
+#ifdef _KERNEL
+extern int pcmcia_add_device __P((struct pcmciadevs *));
+extern int pcmcia_get_cf __P((struct pcmcia_link *, u_char *, int, int,
+ struct pcmcia_conf *));
+extern int pcmcia_targmatch __P((struct device *, struct cfdata *, void *));
+#endif
+
+/* in pcmcia_conf.c, available for user space too: */
+extern int pcmcia_get_cisver1 __P((struct pcmcia_link *, u_char *, int,
+ char *, char *, char *, char *));
+int parse_cfent __P((u_char *, int, int, struct pcmcia_conf *));
+int read_cfg_info __P((u_char *, int, struct pcmcia_conf *));
+void pcmcia_getstr __P((char *buf, u_char **, u_char *));
+
+#endif /* _PCMCIA_PCMCIABUS_H_ */