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 + -
显示快捷键?