aboutsummaryrefslogtreecommitdiff
path: root/sys/pccard/pccard.c
diff options
context:
space:
mode:
authorPoul-Henning Kamp <phk@FreeBSD.org>1995-08-24 08:56:20 +0000
committerPoul-Henning Kamp <phk@FreeBSD.org>1995-08-24 08:56:20 +0000
commit0d2966d3f24fca09ca98c8adc57c8dc4df92571f (patch)
tree79ea479f1d023d0a1dee6bad52acfca88dedd705 /sys/pccard/pccard.c
parent07b1c6b3c9eeffcb879c85170487e92cd1067108 (diff)
downloadsrc-0d2966d3f24fca09ca98c8adc57c8dc4df92571f.tar.gz
src-0d2966d3f24fca09ca98c8adc57c8dc4df92571f.zip
Andrew McRae's pcmcia/pccard code, the kernel part.
This is still very green, but I have managed to get my modem working. Lots of work still to do, but now at least we can commit it. /phk Reviewed by: phk Submitted by: Andrew McRae <andrew@mega.com.au>
Notes
Notes: svn path=/head/; revision=10216
Diffstat (limited to 'sys/pccard/pccard.c')
-rw-r--r--sys/pccard/pccard.c942
1 files changed, 942 insertions, 0 deletions
diff --git a/sys/pccard/pccard.c b/sys/pccard/pccard.c
new file mode 100644
index 000000000000..34d63390ca9f
--- /dev/null
+++ b/sys/pccard/pccard.c
@@ -0,0 +1,942 @@
+/*
+ * pccard.c - Interface code for PC-CARD controllers.
+ *
+ * June 1995, Andrew McRae (andrew@mega.com.au)
+ *-------------------------------------------------------------------------
+ *
+ * Copyright (c) 1995 Andrew McRae. 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. 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 "crd.h"
+#if NCRD > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/ioctl.h>
+#include <sys/syslog.h>
+#include <sys/devconf.h>
+#include <sys/malloc.h>
+
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/icu.h>
+
+#include "apm.h"
+#if NAPM > 0
+#include <machine/apm_bios.h>
+#endif /* NAPM > 0 */
+
+#include <pccard/card.h>
+#include <pccard/slot.h>
+
+#define PCCARD_MEMSIZE (4*1024)
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+/*
+ * cdevsw entry points
+ */
+int crdopen __P((dev_t dev, int oflags, int devtype,
+ struct proc *p));
+int crdclose __P((dev_t dev, int fflag, int devtype,
+ struct proc *p));
+int crdread __P((dev_t dev, struct uio *uio, int ioflag));
+int crdwrite __P((dev_t dev, struct uio *uio, int ioflag));
+int crdioctl __P((dev_t dev, int cmd, caddr_t data,
+ int fflag, struct proc *p));
+int crdselect __P((dev_t dev, int rw, struct proc *p));
+
+static int allocate_driver(struct slot *, struct drv_desc *);
+static void inserted(void *);
+static void disable_slot(struct slot *);
+static int invalid_io_memory(unsigned long, int);
+static struct pccard_drv *find_driver(char *);
+static void remove_device(struct pccard_dev *);
+static void slot_irq_handler(int);
+#if NAPM > 0
+/*
+ * For the APM stuff, the apmhook structure is kept
+ * separate from the slot structure so that the slot
+ * drivers do not need to know about the hooks (or the
+ * data structures).
+ */
+static int slot_suspend(struct slot *sp);
+static int slot_resume(struct slot *sp);
+static struct apmhook s_hook[MAXSLOT]; /* APM suspend */
+static struct apmhook r_hook[MAXSLOT]; /* APM resume */
+#endif /* NAPM > 0 */
+
+void pcic_probe();
+
+static struct slot *pccard_slots[MAXSLOT]; /* slot entries */
+static struct slot *slot_list;
+static struct slot_cont *cont_list;
+static struct pccard_drv *drivers; /* Card drivers */
+/*
+ * The driver interface for read/write uses a block
+ * of memory in the ISA I/O memory space allocated via
+ * an ioctl setting.
+ */
+static unsigned long pccard_mem; /* Physical memory */
+static unsigned char *pccard_kmem; /* Kernel virtual address */
+/*
+ * pccard_configure - called by autoconf code.
+ * Probes for various PC-CARD controllers, and
+ * initialises data structures to point to the
+ * various slots.
+ *
+ * Each controller indicates the number of slots
+ * that it sees, and these are mapped to a master
+ * slot number accessed via the character device entries.
+ */
+void
+pccard_configure()
+{
+struct slot_cont *cp;
+struct slot *sp;
+
+#include "pcic.h"
+#if NPCIC > 0
+ pcic_probe();
+#endif
+}
+/*
+ * pccard_add_driver - Add a new driver to the list of
+ * drivers available for allocation.
+ */
+void
+pccard_add_driver(struct pccard_drv *dp)
+{
+/*
+ * If already loaded, then reject the driver.
+ */
+ if (find_driver(dp->name))
+ {
+ printf("Driver %s already loaded\n", dp->name);
+ return;
+ }
+ dp->next = drivers;
+ drivers = dp;
+}
+/*
+ * pccard_remove_driver - called to unlink driver
+ * from devices. Usually called when drivers are
+ * are unloaded from kernel.
+ */
+void
+pccard_remove_driver(struct pccard_drv *dp)
+{
+struct slot *sp;
+struct pccard_dev *devp, *next;
+struct pccard_drv *drvp;
+
+ for (sp = slot_list; sp; sp = sp->next)
+ for (devp = sp->devices; devp; devp = next)
+ {
+ next = devp->next;
+ if (devp->drv == dp)
+ remove_device(devp);
+ }
+/*
+ * Once all the devices belonging to this driver have been
+ * freed, then remove the driver from the list
+ * of registered drivers.
+ */
+ if (drivers == dp)
+ drivers = dp->next;
+ else
+ for (drvp = drivers; drvp->next; drvp = drvp->next)
+ if (drvp->next == dp)
+ {
+ drvp->next = dp->next;
+ break;
+ }
+}
+/*
+ * pccard_remove_controller - Called when the slot
+ * driver is unloaded. The plan is to unload
+ * drivers from the slots, and then remove the
+ * slots from the slot list, and then finally
+ * remove the controller structure. Messy...
+ */
+void
+pccard_remove_controller(struct slot_cont *cp)
+{
+struct slot *sp, *next, *last = 0;
+struct slot_cont *cl;
+struct pccard_dev *dp;
+
+ for (sp = slot_list; sp; sp = next)
+ {
+ next = sp->next;
+/*
+ * If this slot belongs to this controller,
+ * remove this slot.
+ */
+ if (sp->cinfo == cp)
+ {
+ pccard_slots[sp->slot] = 0;
+ if (sp->insert_timeout)
+ untimeout(inserted, (void *)sp);
+/*
+ * Unload the drivers attached to this slot.
+ */
+ while (dp = sp->devices)
+ remove_device(dp);
+/*
+ * Disable the slot and unlink the slot from the slot list.
+ */
+ disable_slot(sp);
+ if (last)
+ last->next = next;
+ else
+ slot_list = next;
+#if NAPM > 0
+ apm_hook_disestablish(APM_HOOK_SUSPEND,
+ &s_hook[sp->slot]);
+ apm_hook_disestablish(APM_HOOK_RESUME,
+ &r_hook[sp->slot]);
+#endif
+ if (cp->extra && sp->cdata)
+ FREE(sp->cdata, M_DEVBUF);
+ FREE(sp, M_DEVBUF);
+/*
+ * xx Can't use sp after we have freed it.
+ */
+ }
+ else
+ last = sp;
+ }
+/*
+ * Unlink controller structure from controller list.
+ */
+ if (cont_list == cp)
+ cont_list = cp->next;
+ else
+ for (cl = cont_list; cl->next; cl = cl->next)
+ if (cl->next == cp)
+ {
+ cl->next = cp->next;
+ break;
+ }
+}
+
+/*
+ * disable_slot - Disables the slot by removing
+ * the power and unmapping the I/O
+ */
+static void
+disable_slot(struct slot *sp)
+{
+int i;
+struct pccard_dev *devp;
+/*
+ * Unload all the drivers on this slot. Note we can't
+ * call remove_device from here, because this may be called
+ * from the event routine, which is called from the slot
+ * controller's ISR, and this could remove the device
+ * structure out in the middle of some driver activity.
+ *
+ * Note that a race condition is possible here; if a
+ * driver is accessing the device and it is removed, then
+ * all bets are off...
+ */
+ for (devp = sp->devices; devp; devp = devp->next)
+ {
+ devp->drv->unload(devp);
+ devp->running = 0;
+ }
+/*
+ * Power off the slot.
+ */
+ sp->cinfo->disable(sp);
+/*
+ * De-activate all contexts.
+ */
+ for (i = 0; i < sp->cinfo->maxmem; i++)
+ if (sp->mem[i].flags & MDF_ACTIVE)
+ {
+ sp->mem[i].flags = 0;
+ (void)sp->cinfo->mapmem(sp, i);
+ }
+ for (i = 0; i < sp->cinfo->maxio; i++)
+ if (sp->io[i].flags & IODF_ACTIVE)
+ {
+ sp->io[i].flags = 0;
+ (void)sp->cinfo->mapio(sp, i);
+ }
+}
+
+/*
+ * APM hooks for suspending and resuming.
+ */
+#if NAPM > 0
+static int
+slot_suspend(struct slot *sp)
+{
+struct pccard_dev *dp;
+
+ for (dp = sp->devices; dp; dp = dp->next)
+ (void)dp->drv->suspend(dp);
+ sp->cinfo->disable(sp);
+ return(0);
+}
+static int
+slot_resume(struct slot *sp)
+{
+struct pccard_dev *dp;
+
+ sp->cinfo->power(sp);
+ if (sp->irq)
+ sp->cinfo->mapirq(sp, sp->irq);
+ for (dp = sp->devices; dp; dp = dp->next)
+ (void)dp->drv->init(dp, 0);
+ return(0);
+}
+#endif /* NAPM > 0 */
+/*
+ * pccard_alloc_slot - Called from controller probe
+ * routine, this function allocates a new PC-CARD slot
+ * and initialises the data structures using the data provided.
+ * It returns the allocated structure to the probe routine
+ * to allow the controller specific data to be initialised.
+ */
+struct slot *
+pccard_alloc_slot(struct slot_cont *cp)
+{
+struct slot *sp;
+int slotno;
+
+ for (slotno = 0; slotno < MAXSLOT; slotno++)
+ if (pccard_slots[slotno] == 0)
+ break;
+ if (slotno >= MAXSLOT)
+ return(0);
+ MALLOC(sp, struct slot *, sizeof(*sp), M_DEVBUF, M_WAITOK);
+ bzero(sp, sizeof(*sp));
+ if (cp->extra)
+ {
+ MALLOC(sp->cdata, void *, cp->extra, M_DEVBUF, M_WAITOK);
+ bzero(sp->cdata, cp->extra);
+ }
+ sp->cinfo = cp;
+ sp->slot = slotno;
+ pccard_slots[slotno] = sp;
+ sp->next = slot_list;
+ slot_list = sp;
+/*
+ * If this controller hasn't been seen before, then
+ * link it into the list of controllers.
+ */
+ if (cp->slots++ == 0)
+ {
+ cp->next = cont_list;
+ cont_list = cp;
+ if (cp->maxmem > NUM_MEM_WINDOWS)
+ cp->maxmem = NUM_MEM_WINDOWS;
+ if (cp->maxio > NUM_IO_WINDOWS)
+ cp->maxio = NUM_IO_WINDOWS;
+ printf("PC-Card %s (%d mem & %d I/O windows)\n",
+ cp->name, cp->maxmem, cp->maxio);
+ }
+#if NAPM > 0
+ {
+ struct apmhook *ap;
+
+ ap = &s_hook[sp->slot];
+ ap->ah_fun = slot_suspend;
+ ap->ah_arg = (void *) sp;
+ ap->ah_name = cp->name;
+ ap->ah_order = APM_MID_ORDER;
+ apm_hook_establish(APM_HOOK_SUSPEND, ap);
+ ap = &r_hook[sp->slot];
+ ap->ah_fun = slot_resume;
+ ap->ah_arg = (void *) sp;
+ ap->ah_name = cp->name;
+ ap->ah_order = APM_MID_ORDER;
+ apm_hook_establish(APM_HOOK_RESUME, ap);
+ }
+#endif /* NAPM > 0 */
+ return(sp);
+}
+/*
+ * pccard_alloc_intr - allocate an interrupt from the
+ * free interrupts and return its number. The interrupts
+ * allowed are passed as a mask.
+ */
+int
+pccard_alloc_intr(int imask, inthand2_t *hand, int unit, int *maskp)
+{
+int rv, irq;
+unsigned int mask;
+
+ for (irq = 1; irq < 16; irq++)
+ {
+ mask = 1ul << irq;
+ if ((mask & imask) &&
+ register_intr(irq, 0, 0, hand, maskp, unit)==0)
+ {
+ if (maskp)
+ INTRMASK (*maskp, mask);
+
+ update_intr_masks();
+
+ INTREN (mask);
+ return(irq);
+ }
+ }
+ return(-1);
+}
+/*
+ * allocate_driver - Create a new device entry for this
+ * slot, and attach a driver to it.
+ */
+static int
+allocate_driver(struct slot *sp, struct drv_desc *drvp)
+{
+struct pccard_dev *devp;
+struct pccard_drv *dp;
+int err, irq = 0, s;
+
+ dp = find_driver(drvp->name);
+ if (dp == 0)
+ return(ENXIO);
+/*
+ * If an instance of this driver is already installed,
+ * but not running, then remove it. If it is running,
+ * then reject the request.
+ */
+ for (devp = sp->devices; devp; devp = devp->next)
+ if (devp->drv == dp && devp->isahd.id_unit == drvp->unit)
+ {
+ if (devp->running)
+ return(EBUSY);
+ remove_device(devp);
+ break;
+ }
+/*
+ * If an interrupt mask has been given, then check it
+ * against the slot interrupt (if one has been allocated).
+ */
+ if (drvp->irqmask && dp->imask)
+ {
+ if ((sp->cinfo->irqs & drvp->irqmask)==0)
+ return(EINVAL);
+ if (sp->irq)
+ {
+ if (((1 << sp->irq)&drvp->irqmask)==0)
+ return(EINVAL);
+ sp->irqref++;
+ irq = sp->irq;
+ }
+/*
+ * Attempt to allocate an interrupt. XXX We lose at the moment
+ * if the second device relies on a different interrupt mask.
+ */
+ else
+ {
+ irq = pccard_alloc_intr(drvp->irqmask,
+ slot_irq_handler, (int)sp, dp->imask);
+ if (irq < 0)
+ return(EINVAL);
+ sp->irq = irq;
+ sp->irqref = 1;
+ sp->cinfo->mapirq(sp, sp->irq);
+ }
+ }
+ MALLOC(devp, struct pccard_dev *, sizeof(*devp), M_DEVBUF, M_WAITOK);
+ bzero(devp, sizeof(*devp));
+/*
+ * Create an entry for the device under this slot.
+ */
+ devp->drv = dp;
+ devp->sp = sp;
+ devp->isahd.id_unit = drvp->unit;
+ devp->isahd.id_msize = drvp->memsize;
+ devp->isahd.id_iobase = drvp->iobase;
+ if (irq)
+ devp->isahd.id_irq = 1 << irq;
+ devp->isahd.id_flags = drvp->flags;
+/*
+ * Convert the memory to kernel space.
+ */
+ if (drvp->mem)
+ devp->isahd.id_maddr = (caddr_t)(drvp->mem + atdevbase - 0xA0000);
+ else
+ devp->isahd.id_maddr = 0;
+ devp->next = sp->devices;
+ sp->devices = devp;
+ s = splhigh();
+ err = dp->init(devp, 1);
+ splx(s);
+/*
+ * If the init functions returns no error, then the
+ * device has been successfully installed. If so, then
+ * attach it to the slot, otherwise free it and return
+ * the error.
+ */
+ if (err)
+ remove_device(devp);
+ else
+ devp->running = 1;
+ return(err);
+}
+static void
+remove_device(struct pccard_dev *dp)
+{
+struct slot *sp = dp->sp;
+struct pccard_dev *list;
+int s;
+
+/*
+ * If an interrupt is enabled on this slot,
+ * then unregister it if no-one else is using it.
+ */
+ s = splhigh();
+ if (dp->running)
+ dp->drv->unload(dp);
+ if (dp->isahd.id_irq && --sp->irqref == 0)
+ {
+ sp->cinfo->mapirq(sp, 0);
+ unregister_intr(sp->irq, slot_irq_handler);
+ sp->irq = 0;
+ }
+ splx(s);
+/*
+ * Remove from device list on this slot.
+ */
+ if (sp->devices == dp)
+ sp->devices = dp->next;
+ else
+ for (list = sp->devices; list->next; list = list->next)
+ if (list->next == dp)
+ {
+ list->next = dp->next;
+ break;
+ }
+/*
+ * Finally, free the memory space.
+ */
+ FREE(dp, M_DEVBUF);
+}
+/*
+ * card insert routine - Called from a timeout to debounce
+ * insertion events.
+ */
+static void
+inserted(void *arg)
+{
+struct slot *sp = arg;
+
+ sp->insert_timeout = 0;
+ sp->state = filled;
+/*
+ * Enable 5V to the card so that the CIS can be read.
+ */
+ sp->pwr.vcc = 50;
+ sp->pwr.vpp = 0;
+ sp->cinfo->power(sp);
+ printf("Card inserted, slot %d\n", sp->slot);
+/*
+ * Now reset the card.
+ */
+ sp->cinfo->reset(sp);
+}
+/*
+ * Card event callback. Called at splhigh to prevent
+ * device interrupts from interceding.
+ */
+void
+pccard_event(struct slot *sp, enum card_event event)
+{
+int s;
+
+ if (sp->insert_timeout)
+ {
+ sp->insert_timeout = 0;
+ untimeout(inserted, (void *)sp);
+ }
+ switch(event)
+ {
+/*
+ * The slot and devices are disabled, but the
+ * data structures are not unlinked.
+ */
+ case card_removed:
+ if (sp->state == filled)
+ {
+ s = splhigh();
+ disable_slot(sp);
+ sp->state = empty;
+ splx(s);
+ printf("Card removed, slot %d\n", sp->slot);
+ }
+ break;
+ case card_inserted:
+ sp->insert_timeout = 1;
+ timeout(inserted, (void *)sp, hz/4);
+ break;
+ }
+}
+/*
+ * slot_irq_handler - Interrupt handler for shared irq devices.
+ */
+static void
+slot_irq_handler(int sp)
+{
+struct pccard_dev *dp;
+
+/*
+ * For each device that has the shared interrupt,
+ * call the interrupt handler. If the interrupt was
+ * caught, the handler returns true.
+ */
+ for (dp = ((struct slot *)sp)->devices; dp; dp = dp->next)
+ if (dp->isahd.id_irq && dp->running && dp->drv->handler(dp))
+ return;
+ printf("Slot %d, unfielded interrupt (%d)\n",
+ ((struct slot *)sp)->slot, ((struct slot *)sp)->irq);
+}
+/*
+ * Device driver interface.
+ */
+int
+crdopen(dev_t dev, int oflags, int devtype, struct proc *p)
+{
+struct slot *sp;
+
+ if (minor(dev) >= MAXSLOT)
+ return(ENXIO);
+ sp = pccard_slots[minor(dev)];
+ if (sp==0)
+ return(ENXIO);
+ if (sp->rwmem == 0)
+ sp->rwmem = MDF_ATTR;
+ return(0);
+}
+/*
+ * Close doesn't de-allocate any resources, since
+ * slots may be assigned to drivers already.
+ */
+int
+crdclose(dev_t dev, int fflag, int devtype, struct proc *p)
+{
+ return(0);
+}
+/*
+ * read interface. Map memory at lseek offset,
+ * then transfer to user space.
+ */
+int
+crdread(dev_t dev, struct uio *uio, int ioflag)
+{
+struct slot *sp = pccard_slots[minor(dev)];
+unsigned char *p;
+int error = 0, win, count;
+struct mem_desc *mp, oldmap;
+unsigned int offs;
+
+ if (sp == 0 || sp->state != filled)
+ return(ENXIO);
+ if (pccard_mem == 0)
+ return(ENOMEM);
+ for (win = 0; win < sp->cinfo->maxmem; win++)
+ if ((sp->mem[win].flags & MDF_ACTIVE)==0)
+ break;
+ if (win >= sp->cinfo->maxmem)
+ return(EBUSY);
+ mp = &sp->mem[win];
+ oldmap = *mp;
+ mp->flags = sp->rwmem|MDF_ACTIVE;
+#if 0
+ printf("Rd at offs %d, size %d\n", (int)uio->uio_offset,
+ uio->uio_resid);
+#endif
+ while (uio->uio_resid && error == 0)
+ {
+ mp->card = uio->uio_offset;
+ mp->size = PCCARD_MEMSIZE;
+ mp->start = (caddr_t)pccard_mem;
+ if (error = sp->cinfo->mapmem(sp, win))
+ break;
+ offs = (unsigned int)uio->uio_offset & (PCCARD_MEMSIZE - 1);
+ p = pccard_kmem + offs;
+ count = MIN(PCCARD_MEMSIZE - offs, uio->uio_resid);
+ error = uiomove(p, count, uio);
+ }
+/*
+ * Restore original map.
+ */
+ *mp = oldmap;
+ sp->cinfo->mapmem(sp, win);
+
+ return(error);
+}
+/*
+ * crdwrite - Write data to card memory.
+ * Handles wrap around so that only one memory
+ * window is used.
+ */
+int
+crdwrite(dev_t dev, struct uio *uio, int ioflag)
+{
+struct slot *sp = pccard_slots[minor(dev)];
+unsigned char *p, c;
+int error = 0, win, count;
+struct mem_desc *mp, oldmap;
+unsigned int offs;
+
+ if (sp == 0 || sp->state != filled)
+ return(ENXIO);
+ if (pccard_mem == 0)
+ return(ENOMEM);
+ for (win = 0; win < sp->cinfo->maxmem; win++)
+ if ((sp->mem[win].flags & MDF_ACTIVE)==0)
+ break;
+ if (win >= sp->cinfo->maxmem)
+ return(EBUSY);
+ mp = &sp->mem[win];
+ oldmap = *mp;
+ mp->flags = sp->rwmem|MDF_ACTIVE;
+#if 0
+ printf("Wr at offs %d, size %d\n", (int)uio->uio_offset,
+ uio->uio_resid);
+#endif
+ while (uio->uio_resid && error == 0)
+ {
+ mp->card = uio->uio_offset;
+ mp->size = PCCARD_MEMSIZE;
+ mp->start = (caddr_t)pccard_mem;
+ if (error = sp->cinfo->mapmem(sp, win))
+ break;
+ offs = (unsigned int)uio->uio_offset & (PCCARD_MEMSIZE - 1);
+ p = pccard_kmem + offs;
+ count = MIN(PCCARD_MEMSIZE - offs, uio->uio_resid);
+#if 0
+ printf("Writing %d bytes to address 0x%x\n", count, p);
+#endif
+ error = uiomove(p, count, uio);
+ }
+/*
+ * Restore original map.
+ */
+ *mp = oldmap;
+ sp->cinfo->mapmem(sp, win);
+
+ return(error);
+}
+/*
+ * ioctl calls - allows setting/getting of memory and I/O
+ * descriptors, and assignment of drivers.
+ */
+int
+crdioctl(dev_t dev, int cmd, caddr_t data, int fflag, struct proc *p)
+{
+int s;
+struct slot *sp = pccard_slots[minor(dev)];
+struct mem_desc *mp;
+struct io_desc *ip;
+
+ if (sp == 0 && cmd != PIOCRWMEM)
+ return(ENXIO);
+ switch(cmd)
+ {
+ default:
+ if (sp->cinfo->ioctl)
+ return(sp->cinfo->ioctl(sp, cmd, data));
+ return(EINVAL);
+ case PIOCGSTATE:
+ s = splhigh();
+ ((struct slotstate *)data)->state = sp->state;
+ sp->laststate = sp->state;
+ splx(s);
+ ((struct slotstate *)data)->maxmem = sp->cinfo->maxmem;
+ ((struct slotstate *)data)->maxio = sp->cinfo->maxio;
+ ((struct slotstate *)data)->irqs = sp->cinfo->irqs;
+ break;
+/*
+ * Get memory context.
+ */
+ case PIOCGMEM:
+ s = ((struct mem_desc *)data)->window;
+ if (s < 0 || s >= sp->cinfo->maxmem)
+ return(EINVAL);
+ mp = &sp->mem[s];
+ ((struct mem_desc *)data)->flags = mp->flags;
+ ((struct mem_desc *)data)->start = mp->start;
+ ((struct mem_desc *)data)->size = mp->size;
+ ((struct mem_desc *)data)->card = mp->card;
+ break;
+/*
+ * Set memory context. If context already active, then unmap it.
+ * It is hard to see how the parameters can be checked.
+ * At the very least, we only allow root to set the context.
+ */
+ case PIOCSMEM:
+ if (suser(p->p_ucred, &p->p_acflag))
+ return(EPERM);
+ if (sp->state != filled)
+ return(ENXIO);
+ s = ((struct mem_desc *)data)->window;
+ if (s < 0 || s >= sp->cinfo->maxmem)
+ return(EINVAL);
+ sp->mem[s] = *((struct mem_desc *)data);
+ return(sp->cinfo->mapmem(sp, s));
+/*
+ * Get I/O port context.
+ */
+ case PIOCGIO:
+ s = ((struct io_desc *)data)->window;
+ if (s < 0 || s >= sp->cinfo->maxio)
+ return(EINVAL);
+ ip = &sp->io[s];
+ ((struct io_desc *)data)->flags = ip->flags;
+ ((struct io_desc *)data)->start = ip->start;
+ ((struct io_desc *)data)->size = ip->size;
+ break;
+/*
+ * Set I/O port context.
+ */
+ case PIOCSIO:
+ if (suser(p->p_ucred, &p->p_acflag))
+ return(EPERM);
+ if (sp->state != filled)
+ return(ENXIO);
+ s = ((struct io_desc *)data)->window;
+ if (s < 0 || s >= sp->cinfo->maxio)
+ return(EINVAL);
+ sp->io[s] = *((struct io_desc *)data);
+ return(sp->cinfo->mapio(sp, s));
+ break;
+/*
+ * Set memory window flags for read/write interface.
+ */
+ case PIOCRWFLAG:
+ sp->rwmem = *(int *)data;
+ break;
+/*
+ * Set the memory window to be used for the read/write
+ * interface.
+ */
+ case PIOCRWMEM:
+ if (*(unsigned long *)data == 0)
+ {
+ if (pccard_mem)
+ *(unsigned long *)data = pccard_mem;
+ break;
+ }
+ if (suser(p->p_ucred, &p->p_acflag))
+ return(EPERM);
+/*
+ * Validate the memory by checking it against the
+ * I/O memory range. It must also start on an aligned block size.
+ */
+ if (invalid_io_memory(*(unsigned long *)data, PCCARD_MEMSIZE))
+ return(EINVAL);
+ if (*(unsigned long *)data & (PCCARD_MEMSIZE-1))
+ return(EINVAL);
+/*
+ * Map it to kernel VM.
+ */
+ pccard_mem = *(unsigned long *)data;
+ pccard_kmem = (unsigned char *)(pccard_mem
+ + atdevbase - 0xA0000);
+ break;
+/*
+ * Set power values
+ */
+ case PIOCSPOW:
+ sp->pwr = *(struct power *)data;
+ return(sp->cinfo->power(sp));
+/*
+ * Allocate a driver to this slot.
+ */
+ case PIOCSDRV:
+ if (suser(p->p_ucred, &p->p_acflag))
+ return(EPERM);
+ return(allocate_driver(sp, (struct drv_desc *)data));
+ }
+ return(0);
+}
+/*
+ * select - Selects on exceptions will return true
+ * when a change in card status occurs.
+ */
+int
+crdselect(dev_t dev, int rw, struct proc *p)
+{
+int s;
+struct slot *sp = pccard_slots[minor(dev)];
+
+ switch (rw) {
+ case FREAD:
+ return 1;
+ case FWRITE:
+ return 1;
+/*
+ * select for exception - card event.
+ */
+ case 0:
+ s = splhigh();
+ if (sp == 0 || sp->laststate != sp->state)
+ {
+ splx(s);
+ return(1);
+ }
+ selrecord(p, &sp->selp);
+ splx(s);
+ }
+ return(0);
+}
+/*
+ * invalid_io_memory - verify that the ISA I/O memory block
+ * is a valid and unallocated address.
+ * A simple check of the range is done, and then a
+ * search of the current devices is done to check for
+ * overlapping regions.
+ */
+static int
+invalid_io_memory(unsigned long adr, int size)
+{
+ if (adr < 0xC0000 || (adr+size) > 0x100000)
+ return(1);
+ return(0);
+}
+static struct pccard_drv *
+find_driver(char *name)
+{
+struct pccard_drv *dp;
+
+ for (dp = drivers; dp; dp = dp->next)
+ if (strcmp(dp->name, name)==0)
+ return(dp);
+ return(0);
+}
+
+#endif /* NCRD */