📄 pccard.c
字号:
* If an instance of this driver is already installed, * but not running, then remove it. If it is running, * then reject the request. */ for (devi = slt->devices; devi; devi = devi->next) if (devi->drv == drv && devi->isahd.id_unit == desc->unit) { if (devi->running) return(EBUSY); remove_device(devi); break; } /* * If an interrupt mask has been given, then check it * against the slot interrupt (if one has been allocated). */ if (desc->irqmask && drv->imask) { if ((slt->ctrl->irqs & desc->irqmask) == 0) return(EINVAL); if (slt->irq) { if (((1 << slt->irq) & desc->irqmask) == 0) return(EINVAL); slt->irqref++; irq = slt->irq; } else { /* * Attempt to allocate an interrupt. * XXX We lose at the moment if the second * device relies on a different interrupt mask. */ irq = pccard_alloc_intr(desc->irqmask, slot_irq_handler, (int)slt, drv->imask, slt->ctrl->imask); if (irq < 0) return(EINVAL); slt->irq = irq; slt->irqref = 1; slt->ctrl->mapirq(slt, slt->irq); } } MALLOC(devi, struct pccard_devinfo *, sizeof(*devi), M_DEVBUF, M_WAITOK); bzero(devi, sizeof(*devi)); /* * Create an entry for the device under this slot. */ devi->running = 1; devi->drv = drv; devi->slt = slt; devi->isahd.id_irq = irq; devi->isahd.id_unit = desc->unit; devi->isahd.id_msize = desc->memsize; devi->isahd.id_iobase = desc->iobase; bcopy(desc->misc, devi->misc, sizeof(desc->misc)); if (irq) devi->isahd.id_irq = 1 << irq; devi->isahd.id_flags = desc->flags; /* * Convert the memory to kernel space. */ if (desc->mem) devi->isahd.id_maddr = (caddr_t)(void *)(uintptr_t) (desc->mem + atdevbase - IOM_BEGIN); else devi->isahd.id_maddr = 0; devi->next = slt->devices; slt->devices = devi; s = splhigh(); err = drv->enable(devi); splx(s); /* * If the enable 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. We assume that when we free the device, * it will also set 'running' to off. */ if (err) remove_device(devi); return(err);}static voidremove_device(struct pccard_devinfo *devi){ struct slot *slt = devi->slt; struct pccard_devinfo *list; /* * If an interrupt is enabled on this slot, * then unregister it if no-one else is using it. */ unregister_device_interrupt(devi); /* * Remove from device list on this slot. */ if (slt->devices == devi) slt->devices = devi->next; else for (list = slt->devices; list->next; list = list->next) if (list->next == devi) { list->next = devi->next; break; } /* * Finally, free the memory space. */ FREE(devi, M_DEVBUF);}/* * card insert routine - Called from a timeout to debounce * insertion events. */static voidinserted(void *arg){ struct slot *slt = arg; slt->state = filled; /* * Enable 5V to the card so that the CIS can be read. */ slt->pwr.vcc = 50; slt->pwr.vpp = 0; /* * Disable any pending timeouts for this slot, and explicitly * power it off right now. Then, re-enable the power using * the (possibly new) power settings. */ untimeout(power_off_slot, (caddr_t)slt, slt->poff_ch); power_off_slot(slt); slt->ctrl->power(slt); printf("Card inserted, slot %d\n", slt->slotnum); /* * Now start resetting the card. */ slt->ctrl->reset(slt);}/* * Card event callback. Called at splhigh to prevent * device interrupts from interceding. */voidpccard_event(struct slot *slt, enum card_event event){ if (slt->insert_seq) { slt->insert_seq = 0; untimeout(inserted, (void *)slt, slt->insert_ch); } switch(event) { case card_removed: /* * The slot and devices are disabled, but the * data structures are not unlinked. */ if (slt->state == filled) { int s = splhigh(); disable_slot(slt); slt->state = empty; splx(s); printf("Card removed, slot %d\n", slt->slotnum); pccard_remove_beep(); selwakeup(&slt->selp); } break; case card_inserted: slt->insert_seq = 1; slt->insert_ch = timeout(inserted, (void *)slt, hz/4); pccard_remove_beep(); break; }}/* * slot_irq_handler - Interrupt handler for shared irq devices. */static voidslot_irq_handler(void *arg){ struct pccard_devinfo *devi; struct slot *slt = (struct slot *)arg; /* * For each device that has the shared interrupt, * call the interrupt handler. If the interrupt was * caught, the handler returns true. */ for (devi = slt->devices; devi; devi = devi->next) if (devi->isahd.id_irq && devi->running && devi->drv->handler(devi)) return; /* * XXX - Should 'debounce' these for drivers that have recently * been removed. */ printf("Slot %d, unfielded interrupt (%d)\n", slt->slotnum, slt->irq);}/* * Device driver interface. */static intcrdopen(dev_t dev, int oflags, int devtype, struct proc *p){ struct slot *slt; if (minor(dev) >= MAXSLOT) return(ENXIO); slt = pccard_slots[minor(dev)]; if (slt == 0) return(ENXIO); if (slt->rwmem == 0) slt->rwmem = MDF_ATTR; return(0);}/* * Close doesn't de-allocate any resources, since * slots may be assigned to drivers already. */static intcrdclose(dev_t dev, int fflag, int devtype, struct proc *p){ return(0);}/* * read interface. Map memory at lseek offset, * then transfer to user space. */static intcrdread(dev_t dev, struct uio *uio, int ioflag){ struct slot *slt = pccard_slots[minor(dev)]; struct mem_desc *mp, oldmap; unsigned char *p; unsigned int offs; int error = 0, win, count; if (slt == 0 || slt->state != filled) return(ENXIO); if (pccard_mem == 0) return(ENOMEM); for (win = 0; win < slt->ctrl->maxmem; win++) if ((slt->mem[win].flags & MDF_ACTIVE) == 0) break; if (win >= slt->ctrl->maxmem) return(EBUSY); mp = &slt->mem[win]; oldmap = *mp; mp->flags = slt->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)(void *)(uintptr_t)pccard_mem; if (error = slt->ctrl->mapmem(slt, 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; slt->ctrl->mapmem(slt, win); return(error);}/* * crdwrite - Write data to card memory. * Handles wrap around so that only one memory * window is used. */static intcrdwrite(dev_t dev, struct uio *uio, int ioflag){ struct slot *slt = pccard_slots[minor(dev)]; struct mem_desc *mp, oldmap; unsigned char *p; unsigned int offs; int error = 0, win, count; if (slt == 0 || slt->state != filled) return(ENXIO); if (pccard_mem == 0) return(ENOMEM); for (win = 0; win < slt->ctrl->maxmem; win++) if ((slt->mem[win].flags & MDF_ACTIVE)==0) break; if (win >= slt->ctrl->maxmem) return(EBUSY); mp = &slt->mem[win]; oldmap = *mp; mp->flags = slt->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)(void *)(uintptr_t)pccard_mem; if (error = slt->ctrl->mapmem(slt, 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; slt->ctrl->mapmem(slt, win); return(error);}/* * ioctl calls - allows setting/getting of memory and I/O * descriptors, and assignment of drivers. */static intcrdioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p){ struct slot *slt = pccard_slots[minor(dev)]; struct mem_desc *mp; struct io_desc *ip; int s, err; /* beep is disabled until the 1st call of crdioctl() */ pccard_beep_select(BEEP_ON); if (slt == 0 && cmd != PIOCRWMEM) return(ENXIO); switch(cmd) { default: if (slt->ctrl->ioctl) return(slt->ctrl->ioctl(slt, cmd, data)); return(EINVAL); /* * Get slot state. */ case PIOCGSTATE: s = splhigh(); ((struct slotstate *)data)->state = slt->state; ((struct slotstate *)data)->laststate = slt->laststate; slt->laststate = slt->state; splx(s); ((struct slotstate *)data)->maxmem = slt->ctrl->maxmem; ((struct slotstate *)data)->maxio = slt->ctrl->maxio; ((struct slotstate *)data)->irqs = slt->ctrl->irqs; break; /* * Get memory context. */ case PIOCGMEM: s = ((struct mem_desc *)data)->window; if (s < 0 || s >= slt->ctrl->maxmem) return(EINVAL); mp = &slt->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 (slt->state != filled) return(ENXIO); s = ((struct mem_desc *)data)->window; if (s < 0 || s >= slt->ctrl->maxmem) return(EINVAL); slt->mem[s] = *((struct mem_desc *)data); return(slt->ctrl->mapmem(slt, s)); /* * Get I/O port context. */ case PIOCGIO: s = ((struct io_desc *)data)->window; if (s < 0 || s >= slt->ctrl->maxio) return(EINVAL); ip = &slt->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 (slt->state != filled) return(ENXIO); s = ((struct io_desc *)data)->window; if (s < 0 || s >= slt->ctrl->maxio) return(EINVAL); slt->io[s] = *((struct io_desc *)data); return(slt->ctrl->mapio(slt, s)); break; /* * Set memory window flags for read/write interface. */ case PIOCRWFLAG: slt->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 *)(void *)(uintptr_t) (pccard_mem + atdevbase - IOM_BEGIN); break; /* * Set power values. */ case PIOCSPOW: slt->pwr = *(struct power *)data; return(slt->ctrl->power(slt)); /* * Allocate a driver to this slot. */ case PIOCSDRV: if (suser(p->p_ucred, &p->p_acflag)) return(EPERM); err = allocate_driver(slt, (struct dev_desc *)data); if (!err) pccard_success_beep(); else pccard_failure_beep(); return err; case PIOCSBEEP: if (pccard_beep_select(*(int *)data)) { return EINVAL; } break; } return(0);}/* * poll - Poll on exceptions will return true * when a change in card status occurs. */static intcrdpoll(dev_t dev, int events, struct proc *p){ int s; struct slot *slt = pccard_slots[minor(dev)]; int revents = 0; if (events & (POLLIN | POLLRDNORM)) revents |= events & (POLLIN | POLLRDNORM); if (events & (POLLOUT | POLLWRNORM)) revents |= events & (POLLIN | POLLRDNORM); s = splhigh(); /* * select for exception - card event. */ if (events & POLLRDBAND) if (slt == 0 || slt->laststate != slt->state) revents |= POLLRDBAND; if (revents == 0) selrecord(p, &slt->selp); splx(s); return (revents);}/* * 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 intinvalid_io_memory(unsigned long adr, int size){ /* XXX - What's magic about 0xC0000?? */ if (adr < 0xC0000 || (adr+size) > IOM_END) return(1); return(0);}static struct pccard_device *find_driver(char *name){ struct pccard_device *drv; for (drv = drivers; drv; drv = drv->next) if (strcmp(drv->name, name)==0) return(drv); return(0);}static crd_devsw_installed = 0;static voidcrd_drvinit(void *unused){ dev_t dev; if (!crd_devsw_installed) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &crd_cdevsw, NULL); crd_devsw_installed = 1; }}SYSINIT(crddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,crd_drvinit,NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -