stallion.c
来自「linux 内核源代码」· C语言 代码 · 共 2,551 行 · 第 1/5 页
C
2,551 行
pos[(MAXLINE - 1)] = '\n'; return MAXLINE;}/*****************************************************************************//* * Port info, read from the /proc file system. */static int stl_readproc(char *page, char **start, off_t off, int count, int *eof, void *data){ struct stlbrd *brdp; struct stlpanel *panelp; struct stlport *portp; unsigned int brdnr, panelnr, portnr; int totalport, curoff, maxoff; char *pos; pr_debug("stl_readproc(page=%p,start=%p,off=%lx,count=%d,eof=%p," "data=%p\n", page, start, off, count, eof, data); pos = page; totalport = 0; curoff = 0; if (off == 0) { pos += sprintf(pos, "%s: version %s", stl_drvtitle, stl_drvversion); while (pos < (page + MAXLINE - 1)) *pos++ = ' '; *pos++ = '\n'; } curoff = MAXLINE;/* * We scan through for each board, panel and port. The offset is * calculated on the fly, and irrelevant ports are skipped. */ for (brdnr = 0; brdnr < stl_nrbrds; brdnr++) { brdp = stl_brds[brdnr]; if (brdp == NULL) continue; if (brdp->state == 0) continue; maxoff = curoff + (brdp->nrports * MAXLINE); if (off >= maxoff) { curoff = maxoff; continue; } totalport = brdnr * STL_MAXPORTS; for (panelnr = 0; panelnr < brdp->nrpanels; panelnr++) { panelp = brdp->panels[panelnr]; if (panelp == NULL) continue; maxoff = curoff + (panelp->nrports * MAXLINE); if (off >= maxoff) { curoff = maxoff; totalport += panelp->nrports; continue; } for (portnr = 0; portnr < panelp->nrports; portnr++, totalport++) { portp = panelp->ports[portnr]; if (portp == NULL) continue; if (off >= (curoff += MAXLINE)) continue; if ((pos - page + MAXLINE) > count) goto stl_readdone; pos += stl_portinfo(portp, totalport, pos); } } } *eof = 1;stl_readdone: *start = page; return pos - page;}/*****************************************************************************//* * All board interrupts are vectored through here first. This code then * calls off to the approrpriate board interrupt handlers. */static irqreturn_t stl_intr(int irq, void *dev_id){ struct stlbrd *brdp = dev_id; pr_debug("stl_intr(brdp=%p,irq=%d)\n", brdp, irq); return IRQ_RETVAL((* brdp->isr)(brdp));}/*****************************************************************************//* * Interrupt service routine for EasyIO board types. */static int stl_eiointr(struct stlbrd *brdp){ struct stlpanel *panelp; unsigned int iobase; int handled = 0; spin_lock(&brd_lock); panelp = brdp->panels[0]; iobase = panelp->iobase; while (inb(brdp->iostatus) & EIO_INTRPEND) { handled = 1; (* panelp->isr)(panelp, iobase); } spin_unlock(&brd_lock); return handled;}/*****************************************************************************//* * Interrupt service routine for ECH-AT board types. */static int stl_echatintr(struct stlbrd *brdp){ struct stlpanel *panelp; unsigned int ioaddr, bnknr; int handled = 0; outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl); while (inb(brdp->iostatus) & ECH_INTRPEND) { handled = 1; for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { ioaddr = brdp->bnkstataddr[bnknr]; if (inb(ioaddr) & ECH_PNLINTRPEND) { panelp = brdp->bnk2panel[bnknr]; (* panelp->isr)(panelp, (ioaddr & 0xfffc)); } } } outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); return handled;}/*****************************************************************************//* * Interrupt service routine for ECH-MCA board types. */static int stl_echmcaintr(struct stlbrd *brdp){ struct stlpanel *panelp; unsigned int ioaddr, bnknr; int handled = 0; while (inb(brdp->iostatus) & ECH_INTRPEND) { handled = 1; for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { ioaddr = brdp->bnkstataddr[bnknr]; if (inb(ioaddr) & ECH_PNLINTRPEND) { panelp = brdp->bnk2panel[bnknr]; (* panelp->isr)(panelp, (ioaddr & 0xfffc)); } } } return handled;}/*****************************************************************************//* * Interrupt service routine for ECH-PCI board types. */static int stl_echpciintr(struct stlbrd *brdp){ struct stlpanel *panelp; unsigned int ioaddr, bnknr, recheck; int handled = 0; while (1) { recheck = 0; for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { outb(brdp->bnkpageaddr[bnknr], brdp->ioctrl); ioaddr = brdp->bnkstataddr[bnknr]; if (inb(ioaddr) & ECH_PNLINTRPEND) { panelp = brdp->bnk2panel[bnknr]; (* panelp->isr)(panelp, (ioaddr & 0xfffc)); recheck++; handled = 1; } } if (! recheck) break; } return handled;}/*****************************************************************************//* * Interrupt service routine for ECH-8/64-PCI board types. */static int stl_echpci64intr(struct stlbrd *brdp){ struct stlpanel *panelp; unsigned int ioaddr, bnknr; int handled = 0; while (inb(brdp->ioctrl) & 0x1) { handled = 1; for (bnknr = 0; bnknr < brdp->nrbnks; bnknr++) { ioaddr = brdp->bnkstataddr[bnknr]; if (inb(ioaddr) & ECH_PNLINTRPEND) { panelp = brdp->bnk2panel[bnknr]; (* panelp->isr)(panelp, (ioaddr & 0xfffc)); } } } return handled;}/*****************************************************************************//* * Service an off-level request for some channel. */static void stl_offintr(struct work_struct *work){ struct stlport *portp = container_of(work, struct stlport, tqueue); struct tty_struct *tty; unsigned int oldsigs; pr_debug("stl_offintr(portp=%p)\n", portp); if (portp == NULL) return; tty = portp->tty; if (tty == NULL) return; if (test_bit(ASYI_TXLOW, &portp->istate)) tty_wakeup(tty); if (test_bit(ASYI_DCDCHANGE, &portp->istate)) { clear_bit(ASYI_DCDCHANGE, &portp->istate); oldsigs = portp->sigs; portp->sigs = stl_getsignals(portp); if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) wake_up_interruptible(&portp->open_wait); if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) if (portp->flags & ASYNC_CHECK_CD) tty_hangup(tty); /* FIXME: module removal race here - AKPM */ }}/*****************************************************************************//* * Initialize all the ports on a panel. */static int __devinit stl_initports(struct stlbrd *brdp, struct stlpanel *panelp){ struct stlport *portp; unsigned int i; int chipmask; pr_debug("stl_initports(brdp=%p,panelp=%p)\n", brdp, panelp); chipmask = stl_panelinit(brdp, panelp);/* * All UART's are initialized (if found!). Now go through and setup * each ports data structures. */ for (i = 0; i < panelp->nrports; i++) { portp = kzalloc(sizeof(struct stlport), GFP_KERNEL); if (!portp) { printk("STALLION: failed to allocate memory " "(size=%Zd)\n", sizeof(struct stlport)); break; } portp->magic = STL_PORTMAGIC; portp->portnr = i; portp->brdnr = panelp->brdnr; portp->panelnr = panelp->panelnr; portp->uartp = panelp->uartp; portp->clk = brdp->clk; portp->baud_base = STL_BAUDBASE; portp->close_delay = STL_CLOSEDELAY; portp->closing_wait = 30 * HZ; INIT_WORK(&portp->tqueue, stl_offintr); init_waitqueue_head(&portp->open_wait); init_waitqueue_head(&portp->close_wait); portp->stats.brd = portp->brdnr; portp->stats.panel = portp->panelnr; portp->stats.port = portp->portnr; panelp->ports[i] = portp; stl_portinit(brdp, panelp, portp); } return 0;}static void stl_cleanup_panels(struct stlbrd *brdp){ struct stlpanel *panelp; struct stlport *portp; unsigned int j, k; for (j = 0; j < STL_MAXPANELS; j++) { panelp = brdp->panels[j]; if (panelp == NULL) continue; for (k = 0; k < STL_PORTSPERPANEL; k++) { portp = panelp->ports[k]; if (portp == NULL) continue; if (portp->tty != NULL) stl_hangup(portp->tty); kfree(portp->tx.buf); kfree(portp); } kfree(panelp); }}/*****************************************************************************//* * Try to find and initialize an EasyIO board. */static int __devinit stl_initeio(struct stlbrd *brdp){ struct stlpanel *panelp; unsigned int status; char *name; int retval; pr_debug("stl_initeio(brdp=%p)\n", brdp); brdp->ioctrl = brdp->ioaddr1 + 1; brdp->iostatus = brdp->ioaddr1 + 2; status = inb(brdp->iostatus); if ((status & EIO_IDBITMASK) == EIO_MK3) brdp->ioctrl++;/* * Handle board specific stuff now. The real difference is PCI * or not PCI. */ if (brdp->brdtype == BRD_EASYIOPCI) { brdp->iosize1 = 0x80; brdp->iosize2 = 0x80; name = "serial(EIO-PCI)"; outb(0x41, (brdp->ioaddr2 + 0x4c)); } else { brdp->iosize1 = 8; name = "serial(EIO)"; if ((brdp->irq < 0) || (brdp->irq > 15) || (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); retval = -EINVAL; goto err; } outb((stl_vecmap[brdp->irq] | EIO_0WS | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl); } retval = -EBUSY; if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) { printk(KERN_WARNING "STALLION: Warning, board %d I/O address " "%x conflicts with another device\n", brdp->brdnr, brdp->ioaddr1); goto err; } if (brdp->iosize2 > 0) if (!request_region(brdp->ioaddr2, brdp->iosize2, name)) { printk(KERN_WARNING "STALLION: Warning, board %d I/O " "address %x conflicts with another device\n", brdp->brdnr, brdp->ioaddr2); printk(KERN_WARNING "STALLION: Warning, also " "releasing board %d I/O address %x \n", brdp->brdnr, brdp->ioaddr1); goto err_rel1; }/* * Everything looks OK, so let's go ahead and probe for the hardware. */ brdp->clk = CD1400_CLK; brdp->isr = stl_eiointr; retval = -ENODEV; switch (status & EIO_IDBITMASK) { case EIO_8PORTM: brdp->clk = CD1400_CLK8M; /* fall thru */ case EIO_8PORTRS: case EIO_8PORTDI: brdp->nrports = 8; break; case EIO_4PORTRS: brdp->nrports = 4; break; case EIO_MK3: switch (status & EIO_BRDMASK) { case ID_BRD4: brdp->nrports = 4; break; case ID_BRD8: brdp->nrports = 8; break; case ID_BRD16: brdp->nrports = 16; break; default: goto err_rel2; } break; default: goto err_rel2; }/* * We have verified that the board is actually present, so now we * can complete the setup. */ panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL); if (!panelp) { printk(KERN_WARNING "STALLION: failed to allocate memory " "(size=%Zd)\n", sizeof(struct stlpanel)); retval = -ENOMEM; goto err_rel2; } panelp->magic = STL_PANELMAGIC; panelp->brdnr = brdp->brdnr; panelp->panelnr = 0; panelp->nrports = brdp->nrports; panelp->iobase = brdp->ioaddr1; panelp->hwid = status; if ((status & EIO_IDBITMASK) == EIO_MK3) { panelp->uartp = &stl_sc26198uart; panelp->isr = stl_sc26198intr; } else { panelp->uartp = &stl_cd1400uart; panelp->isr = stl_cd1400eiointr; } brdp->panels[0] = panelp; brdp->nrpanels = 1; brdp->state |= BRD_FOUND; brdp->hwid = status; if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) { printk("STALLION: failed to register interrupt " "routine for %s irq=%d\n", name, brdp->irq); retval = -ENODEV; goto err_fr; } return 0;err_fr: stl_cleanup_panels(brdp);err_rel2: if (brdp->iosize2 > 0) release_region(brdp->ioaddr2, brdp->iosize2);err_rel1: release_region(brdp->ioaddr1, brdp->iosize1);err: return retval;}/*****************************************************************************//* * Try to find an ECH board and initialize it. This code is capable of * dealing with all types of ECH board. */static int __devinit stl_initech(struct stlbrd *brdp){ struct stlpanel *panelp; unsigned int status, nxtid, ioaddr, conflict, panelnr, banknr, i; int retval; char *name;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?