📄 pccard.c
字号:
/* * 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. * * $Id: pccard.c,v 1.68.2.3 1999/04/27 18:39:23 jdp Exp $ */#include "opt_devfs.h"#include "opt_pcic.h"#include <sys/param.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/proc.h>#include <sys/malloc.h>#include <sys/sysctl.h>#include <sys/conf.h>#ifdef DEVFS#include <sys/devfsext.h>#endif /*DEVFS*/#include <sys/uio.h>#include <sys/poll.h>#include <sys/interrupt.h>#include <i386/isa/isa_device.h>#include <i386/isa/icu.h>#include <i386/isa/intr_machdep.h>#include "apm.h"#if NAPM > 0#include <machine/apm_bios.h>#endif /* NAPM > 0 */#include <pccard/cardinfo.h>#include <pccard/driver.h>#include <pccard/slot.h>#include <machine/md_var.h>/* * XXX We shouldn't be using processor-specific/bus-specific code in * here, but we need the start of the ISA hole (IOM_BEGIN). */#ifdef PC98#include <pc98/pc98/pc98.h>#else#include <i386/isa/isa.h>#endifSYSCTL_NODE(_machdep, OID_AUTO, pccard, CTLFLAG_RW, 0, "pccard");static int pcic_resume_reset =#ifdef PCIC_RESUME_RESET /* opt_pcic.h */ 1;#else 0;#endifSYSCTL_INT(_machdep_pccard, OID_AUTO, pcic_resume_reset, CTLFLAG_RW, &pcic_resume_reset, 0, "");#define PCCARD_MEMSIZE (4*1024)#define MIN(a,b) ((a)<(b)?(a):(b))static int allocate_driver(struct slot *, struct dev_desc *);static void inserted(void *);static void unregister_device_interrupt(struct pccard_devinfo *);static void disable_slot(struct slot *);static int invalid_io_memory(unsigned long, int);static struct pccard_device *find_driver(char *);static void remove_device(struct pccard_devinfo *);static inthand2_t slot_irq_handler;static void power_off_slot(void *);static void pccard_configure(void *);SYSINIT(pccard, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE + 1, pccard_configure, NULL);#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(void *arg);static int slot_resume(void *arg);static struct apmhook s_hook[MAXSLOT]; /* APM suspend */static struct apmhook r_hook[MAXSLOT]; /* APM resume */#endif /* NAPM > 0 */static struct slot *pccard_slots[MAXSLOT]; /* slot entries */static struct slot *slot_list;static struct slot_ctrl *cont_list;static struct pccard_device *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 */static d_open_t crdopen;static d_close_t crdclose;static d_read_t crdread;static d_write_t crdwrite;static d_ioctl_t crdioctl;static d_poll_t crdpoll;#define CDEV_MAJOR 50static struct cdevsw crd_cdevsw = { crdopen, crdclose, crdread, crdwrite, /*50*/ crdioctl, nostop, nullreset, nodevtotty,/* pcmcia */ crdpoll, nommap, NULL, "crd", NULL, -1 };/* * 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. * * XXX this is a relic. Each controller has it's own probe * configuration hook. Printing a list of configured devices * with pccard support probably isn't all that useful. */static voidpccard_configure(dummy) void *dummy;{ struct pccard_device *drv; /* This isn't strictly correct, but works because of initialize order */ printf("Initializing PC-card drivers:"); for (drv = drivers; drv != NULL; drv = drv->next) printf(" %s", drv->name); printf("\n");}intpccard_module_handler(module_t mod, int what, void *arg){ struct pccard_device *drv = (struct pccard_device *)arg; switch(what) { case MOD_LOAD: pccard_add_driver(drv); break; case MOD_UNLOAD: pccard_remove_driver(drv); break; default: break; } return 0;}/* * pccard_add_driver - Add a new driver to the list of * drivers available for allocation. */voidpccard_add_driver(struct pccard_device *drv){ /* * If already loaded, then reject the driver. */ if (find_driver(drv->name)) { printf("Driver %s already loaded\n", drv->name); return; } drv->next = drivers; drivers = drv;}/* * pccard_remove_driver - called to unlink driver * from devices. Usually called when drivers are * unloaded from kernel. */voidpccard_remove_driver(struct pccard_device *drv){ struct slot *slt; struct pccard_devinfo *devi, *next; struct pccard_device *drvlist; for (slt = slot_list; slt; slt = slt->next) for (devi = slt->devices; devi; devi = next) { next = devi->next; if (devi->drv == drv) remove_device(devi); } /* * Once all the devices belonging to this driver have been * freed, then remove the driver from the list * of registered drivers. */ if (drivers == drv) drivers = drv->next; else for (drvlist = drivers; drvlist->next; drvlist = drvlist->next) if (drvlist->next == drv) { drvlist->next = drv->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... */voidpccard_remove_controller(struct slot_ctrl *ctrl){ struct slot *slt, *next, *last = 0; struct slot_ctrl *cl; struct pccard_devinfo *devi; for (slt = slot_list; slt; slt = next) { next = slt->next; /* * If this slot belongs to this controller, * remove this slot. */ if (slt->ctrl == ctrl) { pccard_slots[slt->slotnum] = 0; if (slt->insert_seq) untimeout(inserted, (void *)slt, slt->insert_ch); /* * Unload the drivers attached to this slot. */ while (devi = slt->devices) remove_device(devi); /* * Disable the slot and unlink the slot from the * slot list. */ disable_slot(slt); if (last) last->next = next; else slot_list = next;#if NAPM > 0 apm_hook_disestablish(APM_HOOK_SUSPEND, &s_hook[slt->slotnum]); apm_hook_disestablish(APM_HOOK_RESUME, &r_hook[slt->slotnum]);#endif if (ctrl->extra && slt->cdata) FREE(slt->cdata, M_DEVBUF); FREE(slt, M_DEVBUF); /* * Can't use slot after we have freed it. */ } else { last = slt; } } /* * Unlink controller structure from controller list. */ if (cont_list == ctrl) cont_list = ctrl->next; else for (cl = cont_list; cl->next; cl = cl->next) if (cl->next == ctrl) { cl->next = ctrl->next; break; }}/* * Power off the slot. * (doing it immediately makes the removal of some cards unstable) */static voidpower_off_slot(void *arg){ struct slot *slt = (struct slot *)arg; /* Power off the slot. */ slt->pwr_off_pending = 0; slt->ctrl->disable(slt);}/* * unregister_device_interrupt - Disable the interrupt generation to * the device driver which is handling it, so we can remove it. */static voidunregister_device_interrupt(struct pccard_devinfo *devi){ struct slot *slt = devi->slt; int s; if (devi->running) { s = splhigh(); devi->drv->disable(devi); devi->running = 0; if (devi->isahd.id_irq && --slt->irqref <= 0) { printf("Return IRQ=%d\n",slt->irq); slt->ctrl->mapirq(slt, 0); INTRDIS(1<<slt->irq); unregister_intr(slt->irq, slot_irq_handler); if (devi->drv->imask) INTRUNMASK(*devi->drv->imask,(1<<slt->irq)); /* Remove from the PCIC controller imask */ if (slt->ctrl->imask) INTRUNMASK(*(slt->ctrl->imask), (1<<slt->irq)); slt->irq = 0; } splx(s); }}/* * disable_slot - Disables the slot by removing * the power and unmapping the I/O */static voiddisable_slot(struct slot *slt){ struct pccard_devinfo *devi; int i; /* * Unload all the drivers on this slot. Note we can't * remove the device structures themselves, because this * may be called from the event routine, which is called * from the slot controller's ISR, and removing the structures * shouldn't happen during 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 (devi = slt->devices; devi; devi = devi->next) unregister_device_interrupt(devi); /* Power off the slot 1/2 second after removal of the card */ slt->poff_ch = timeout(power_off_slot, (caddr_t)slt, hz / 2); slt->pwr_off_pending = 1; /* De-activate all contexts. */ for (i = 0; i < slt->ctrl->maxmem; i++) if (slt->mem[i].flags & MDF_ACTIVE) { slt->mem[i].flags = 0; (void)slt->ctrl->mapmem(slt, i); } for (i = 0; i < slt->ctrl->maxio; i++) if (slt->io[i].flags & IODF_ACTIVE) { slt->io[i].flags = 0; (void)slt->ctrl->mapio(slt, i); }}/* * APM hooks for suspending and resuming. */#if NAPM > 0static intslot_suspend(void *arg){ struct slot *slt = arg; /* This code stolen from pccard_event:card_removed */ if (slt->state == filled) { int s = splhigh(); disable_slot(slt); slt->laststate = filled; slt->state = suspend; splx(s); printf("Card disabled, slot %d\n", slt->slotnum); } /* * Disable any pending timeouts for this slot since we're * powering it down/disabling now. */ untimeout(power_off_slot, (caddr_t)slt, slt->poff_ch); slt->ctrl->disable(slt); return (0);}static intslot_resume(void *arg){ struct slot *slt = arg; if (pcic_resume_reset) slt->ctrl->resume(slt); /* This code stolen from pccard_event:card_inserted */ if (slt->state == suspend) { slt->laststate = suspend; slt->state = empty; slt->insert_seq = 1; untimeout(inserted, (void *)slt, slt->insert_ch); slt->insert_ch = timeout(inserted, (void *)slt, hz/4); selwakeup(&slt->selp); } 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_ctrl *ctrl){ struct slot *slt; int slotno; for (slotno = 0; slotno < MAXSLOT; slotno++) if (pccard_slots[slotno] == 0) break; if (slotno == MAXSLOT) return(0); MALLOC(slt, struct slot *, sizeof(*slt), M_DEVBUF, M_WAITOK); bzero(slt, sizeof(*slt));#ifdef DEVFS slt->devfs_token = devfs_add_devswf(&crd_cdevsw, slotno, DV_CHR, 0, 0, 0600, "card%d", slotno);#endif if (ctrl->extra) { MALLOC(slt->cdata, void *, ctrl->extra, M_DEVBUF, M_WAITOK); bzero(slt->cdata, ctrl->extra); } slt->ctrl = ctrl; slt->slotnum = slotno; pccard_slots[slotno] = slt; slt->next = slot_list; slot_list = slt; /* * If this controller hasn't been seen before, then * link it into the list of controllers. */ if (ctrl->slots++ == 0) { ctrl->next = cont_list; cont_list = ctrl; if (ctrl->maxmem > NUM_MEM_WINDOWS) ctrl->maxmem = NUM_MEM_WINDOWS; if (ctrl->maxio > NUM_IO_WINDOWS) ctrl->maxio = NUM_IO_WINDOWS; printf("PC-Card %s (%d mem & %d I/O windows)\n", ctrl->name, ctrl->maxmem, ctrl->maxio); } callout_handle_init(&slt->insert_ch); callout_handle_init(&slt->poff_ch);#if NAPM > 0 { struct apmhook *ap; ap = &s_hook[slt->slotnum]; ap->ah_fun = slot_suspend; ap->ah_arg = (void *)slt; ap->ah_name = ctrl->name; ap->ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_SUSPEND, ap); ap = &r_hook[slt->slotnum]; ap->ah_fun = slot_resume; ap->ah_arg = (void *)slt; ap->ah_name = ctrl->name; ap->ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME, ap); }#endif /* NAPM > 0 */ return(slt);}/* * pccard_alloc_intr - allocate an interrupt from the * free interrupts and return its number. The interrupts * allowed are passed as a mask. */intpccard_alloc_intr(u_int imask, inthand2_t *hand, int unit, u_int *maskp, u_int *pcic_imask){ int irq; unsigned int mask; for (irq = 1; irq < ICU_LEN; irq++) { mask = 1ul << irq; if (!(mask & imask)) continue; INTRMASK(*maskp, mask); if (register_intr(irq, 0, 0, hand, maskp, unit) == 0) { /* add this to the PCIC controller's mask */ if (pcic_imask) INTRMASK(*pcic_imask, (1 << irq)); update_intr_masks(); INTREN(mask); return(irq); } /* No luck, remove from mask again... */ INTRUNMASK(*maskp, mask); update_intr_masks(); } return(-1);}/* * allocate_driver - Create a new device entry for this * slot, and attach a driver to it. */static intallocate_driver(struct slot *slt, struct dev_desc *desc){ struct pccard_devinfo *devi; struct pccard_device *drv; int err, irq = 0, s; drv = find_driver(desc->name); if (drv == 0) return(ENXIO); /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -