diff options
author | Poul-Henning Kamp <phk@FreeBSD.org> | 1995-08-24 08:56:20 +0000 |
---|---|---|
committer | Poul-Henning Kamp <phk@FreeBSD.org> | 1995-08-24 08:56:20 +0000 |
commit | 0d2966d3f24fca09ca98c8adc57c8dc4df92571f (patch) | |
tree | 79ea479f1d023d0a1dee6bad52acfca88dedd705 /sys/pccard/pccard.c | |
parent | 07b1c6b3c9eeffcb879c85170487e92cd1067108 (diff) | |
download | src-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.c | 942 |
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 */ |