stallion.c

来自「linux 内核源代码」· C语言 代码 · 共 2,551 行 · 第 1/5 页

C
2,551
字号
	pr_debug("stl_initech(brdp=%p)\n", brdp);	status = 0;	conflict = 0;/* *	Set up the initial board register contents for boards. This varies a *	bit between the different board types. So we need to handle each *	separately. Also do a check that the supplied IRQ is good. */	switch (brdp->brdtype) {	case BRD_ECH:		brdp->isr = stl_echatintr;		brdp->ioctrl = brdp->ioaddr1 + 1;		brdp->iostatus = brdp->ioaddr1 + 1;		status = inb(brdp->iostatus);		if ((status & ECH_IDBITMASK) != ECH_ID) {			retval = -ENODEV;			goto err;		}		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;		}		status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);		status |= (stl_vecmap[brdp->irq] << 1);		outb((status | ECH_BRDRESET), brdp->ioaddr1);		brdp->ioctrlval = ECH_INTENABLE |			((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);		for (i = 0; i < 10; i++)			outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);		brdp->iosize1 = 2;		brdp->iosize2 = 32;		name = "serial(EC8/32)";		outb(status, brdp->ioaddr1);		break;	case BRD_ECHMC:		brdp->isr = stl_echmcaintr;		brdp->ioctrl = brdp->ioaddr1 + 0x20;		brdp->iostatus = brdp->ioctrl;		status = inb(brdp->iostatus);		if ((status & ECH_IDBITMASK) != ECH_ID) {			retval = -ENODEV;			goto err;		}		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(ECHMC_BRDRESET, brdp->ioctrl);		outb(ECHMC_INTENABLE, brdp->ioctrl);		brdp->iosize1 = 64;		name = "serial(EC8/32-MC)";		break;	case BRD_ECHPCI:		brdp->isr = stl_echpciintr;		brdp->ioctrl = brdp->ioaddr1 + 2;		brdp->iosize1 = 4;		brdp->iosize2 = 8;		name = "serial(EC8/32-PCI)";		break;	case BRD_ECH64PCI:		brdp->isr = stl_echpci64intr;		brdp->ioctrl = brdp->ioaddr2 + 0x40;		outb(0x43, (brdp->ioaddr1 + 0x4c));		brdp->iosize1 = 0x80;		brdp->iosize2 = 0x80;		name = "serial(EC8/64-PCI)";		break;	default:		printk("STALLION: unknown board type=%d\n", brdp->brdtype);		retval = -EINVAL;		goto err;	}/* *	Check boards for possible IO address conflicts and return fail status  * 	if an IO conflict found. */	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;		}/* *	Scan through the secondary io address space looking for panels. *	As we find'em allocate and initialize panel structures for each. */	brdp->clk = CD1400_CLK;	brdp->hwid = status;	ioaddr = brdp->ioaddr2;	banknr = 0;	panelnr = 0;	nxtid = 0;	for (i = 0; i < STL_MAXPANELS; i++) {		if (brdp->brdtype == BRD_ECHPCI) {			outb(nxtid, brdp->ioctrl);			ioaddr = brdp->ioaddr2;		}		status = inb(ioaddr + ECH_PNLSTATUS);		if ((status & ECH_PNLIDMASK) != nxtid)			break;		panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL);		if (!panelp) {			printk("STALLION: failed to allocate memory "				"(size=%Zd)\n", sizeof(struct stlpanel));			retval = -ENOMEM;			goto err_fr;		}		panelp->magic = STL_PANELMAGIC;		panelp->brdnr = brdp->brdnr;		panelp->panelnr = panelnr;		panelp->iobase = ioaddr;		panelp->pagenr = nxtid;		panelp->hwid = status;		brdp->bnk2panel[banknr] = panelp;		brdp->bnkpageaddr[banknr] = nxtid;		brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS;		if (status & ECH_PNLXPID) {			panelp->uartp = &stl_sc26198uart;			panelp->isr = stl_sc26198intr;			if (status & ECH_PNL16PORT) {				panelp->nrports = 16;				brdp->bnk2panel[banknr] = panelp;				brdp->bnkpageaddr[banknr] = nxtid;				brdp->bnkstataddr[banknr++] = ioaddr + 4 +					ECH_PNLSTATUS;			} else				panelp->nrports = 8;		} else {			panelp->uartp = &stl_cd1400uart;			panelp->isr = stl_cd1400echintr;			if (status & ECH_PNL16PORT) {				panelp->nrports = 16;				panelp->ackmask = 0x80;				if (brdp->brdtype != BRD_ECHPCI)					ioaddr += EREG_BANKSIZE;				brdp->bnk2panel[banknr] = panelp;				brdp->bnkpageaddr[banknr] = ++nxtid;				brdp->bnkstataddr[banknr++] = ioaddr +					ECH_PNLSTATUS;			} else {				panelp->nrports = 8;				panelp->ackmask = 0xc0;			}		}		nxtid++;		ioaddr += EREG_BANKSIZE;		brdp->nrports += panelp->nrports;		brdp->panels[panelnr++] = panelp;		if ((brdp->brdtype != BRD_ECHPCI) &&		    (ioaddr >= (brdp->ioaddr2 + brdp->iosize2))) {			retval = -EINVAL;			goto err_fr;		}	}	brdp->nrpanels = panelnr;	brdp->nrbnks = banknr;	if (brdp->brdtype == BRD_ECH)		outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);	brdp->state |= BRD_FOUND;	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);	if (brdp->iosize2 > 0)		release_region(brdp->ioaddr2, brdp->iosize2);err_rel1:	release_region(brdp->ioaddr1, brdp->iosize1);err:	return retval;}/*****************************************************************************//* *	Initialize and configure the specified board. *	Scan through all the boards in the configuration and see what we *	can find. Handle EIO and the ECH boards a little differently here *	since the initial search and setup is very different. */static int __devinit stl_brdinit(struct stlbrd *brdp){	int i, retval;	pr_debug("stl_brdinit(brdp=%p)\n", brdp);	switch (brdp->brdtype) {	case BRD_EASYIO:	case BRD_EASYIOPCI:		retval = stl_initeio(brdp);		if (retval)			goto err;		break;	case BRD_ECH:	case BRD_ECHMC:	case BRD_ECHPCI:	case BRD_ECH64PCI:		retval = stl_initech(brdp);		if (retval)			goto err;		break;	default:		printk("STALLION: board=%d is unknown board type=%d\n",			brdp->brdnr, brdp->brdtype);		retval = -ENODEV;		goto err;	}	if ((brdp->state & BRD_FOUND) == 0) {		printk("STALLION: %s board not found, board=%d io=%x irq=%d\n",			stl_brdnames[brdp->brdtype], brdp->brdnr,			brdp->ioaddr1, brdp->irq);		goto err_free;	}	for (i = 0; i < STL_MAXPANELS; i++)		if (brdp->panels[i] != NULL)			stl_initports(brdp, brdp->panels[i]);	printk("STALLION: %s found, board=%d io=%x irq=%d "		"nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype],		brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels,		brdp->nrports);	return 0;err_free:	free_irq(brdp->irq, brdp);	stl_cleanup_panels(brdp);	release_region(brdp->ioaddr1, brdp->iosize1);	if (brdp->iosize2 > 0)		release_region(brdp->ioaddr2, brdp->iosize2);err:	return retval;}/*****************************************************************************//* *	Find the next available board number that is free. */static int __devinit stl_getbrdnr(void){	unsigned int i;	for (i = 0; i < STL_MAXBRDS; i++)		if (stl_brds[i] == NULL) {			if (i >= stl_nrbrds)				stl_nrbrds = i + 1;			return i;		}	return -1;}/*****************************************************************************//* *	We have a Stallion board. Allocate a board structure and *	initialize it. Read its IO and IRQ resources from PCI *	configuration space. */static int __devinit stl_pciprobe(struct pci_dev *pdev,		const struct pci_device_id *ent){	struct stlbrd *brdp;	unsigned int i, brdtype = ent->driver_data;	int brdnr, retval = -ENODEV;	if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE)		goto err;	retval = pci_enable_device(pdev);	if (retval)		goto err;	brdp = stl_allocbrd();	if (brdp == NULL) {		retval = -ENOMEM;		goto err;	}	mutex_lock(&stl_brdslock);	brdnr = stl_getbrdnr();	if (brdnr < 0) {		dev_err(&pdev->dev, "too many boards found, "			"maximum supported %d\n", STL_MAXBRDS);		mutex_unlock(&stl_brdslock);		retval = -ENODEV;		goto err_fr;	}	brdp->brdnr = (unsigned int)brdnr;	stl_brds[brdp->brdnr] = brdp;	mutex_unlock(&stl_brdslock);	brdp->brdtype = brdtype;	brdp->state |= STL_PROBED;/* *	We have all resources from the board, so let's setup the actual *	board structure now. */	switch (brdtype) {	case BRD_ECHPCI:		brdp->ioaddr2 = pci_resource_start(pdev, 0);		brdp->ioaddr1 = pci_resource_start(pdev, 1);		break;	case BRD_ECH64PCI:		brdp->ioaddr2 = pci_resource_start(pdev, 2);		brdp->ioaddr1 = pci_resource_start(pdev, 1);		break;	case BRD_EASYIOPCI:		brdp->ioaddr1 = pci_resource_start(pdev, 2);		brdp->ioaddr2 = pci_resource_start(pdev, 1);		break;	default:		dev_err(&pdev->dev, "unknown PCI board type=%u\n", brdtype);		break;	}	brdp->irq = pdev->irq;	retval = stl_brdinit(brdp);	if (retval)		goto err_null;	pci_set_drvdata(pdev, brdp);	for (i = 0; i < brdp->nrports; i++)		tty_register_device(stl_serial,				brdp->brdnr * STL_MAXPORTS + i, &pdev->dev);	return 0;err_null:	stl_brds[brdp->brdnr] = NULL;err_fr:	kfree(brdp);err:	return retval;}static void __devexit stl_pciremove(struct pci_dev *pdev){	struct stlbrd *brdp = pci_get_drvdata(pdev);	unsigned int i;	free_irq(brdp->irq, brdp);	stl_cleanup_panels(brdp);	release_region(brdp->ioaddr1, brdp->iosize1);	if (brdp->iosize2 > 0)		release_region(brdp->ioaddr2, brdp->iosize2);	for (i = 0; i < brdp->nrports; i++)		tty_unregister_device(stl_serial,				brdp->brdnr * STL_MAXPORTS + i);	stl_brds[brdp->brdnr] = NULL;	kfree(brdp);}static struct pci_driver stl_pcidriver = {	.name = "stallion",	.id_table = stl_pcibrds,	.probe = stl_pciprobe,	.remove = __devexit_p(stl_pciremove)};/*****************************************************************************//* *	Return the board stats structure to user app. */static int stl_getbrdstats(combrd_t __user *bp){	combrd_t	stl_brdstats;	struct stlbrd	*brdp;	struct stlpanel	*panelp;	unsigned int i;	if (copy_from_user(&stl_brdstats, bp, sizeof(combrd_t)))		return -EFAULT;	if (stl_brdstats.brd >= STL_MAXBRDS)		return -ENODEV;	brdp = stl_brds[stl_brdstats.brd];	if (brdp == NULL)		return -ENODEV;	memset(&stl_brdstats, 0, sizeof(combrd_t));	stl_brdstats.brd = brdp->brdnr;	stl_brdstats.type = brdp->brdtype;	stl_brdstats.hwid = brdp->hwid;	stl_brdstats.state = brdp->state;	stl_brdstats.ioaddr = brdp->ioaddr1;	stl_brdstats.ioaddr2 = brdp->ioaddr2;	stl_brdstats.irq = brdp->irq;	stl_brdstats.nrpanels = brdp->nrpanels;	stl_brdstats.nrports = brdp->nrports;	for (i = 0; i < brdp->nrpanels; i++) {		panelp = brdp->panels[i];		stl_brdstats.panels[i].panel = i;		stl_brdstats.panels[i].hwid = panelp->hwid;		stl_brdstats.panels[i].nrports = panelp->nrports;	}	return copy_to_user(bp, &stl_brdstats, sizeof(combrd_t)) ? -EFAULT : 0;}/*****************************************************************************//* *	Resolve the referenced port number into a port struct pointer. */static struct stlport *stl_getport(int brdnr, int panelnr, int portnr){	struct stlbrd	*brdp;	struct stlpanel	*panelp;	if (brdnr < 0 || brdnr >= STL_MAXBRDS)		return NULL;	brdp = stl_brds[brdnr];	if (brdp == NULL)		return NULL;	if (panelnr < 0 || (unsigned int)panelnr >= brdp->nrpanels)		return NULL;	panelp = brdp->panels[panelnr];	if (panelp == NULL)		return NULL;	if (portnr < 0 || (unsigned int)portnr >= panelp->nrports)		return NULL;	return panelp->ports[portnr];}/*****************************************************************************//* *	Return the port stats structure to user app. A NULL port struct *	pointer passed in means that we need to find out from the app *	what port to get stats for (used through board control device). */static int stl_getportstats(struct stlport *portp, comstats_t __user *cp){	comstats_t	stl_comstats;	unsigned char	*head, *tail;	unsigned long	flags;	if (!portp) {		if (copy_from_user(&stl_comstats, cp, sizeof(comstats_t)))			return -EFAULT;		portp = stl_getport(stl_comstats.brd, stl_comstats.panel,			stl_comstats.port);		if (portp == NULL)			return -ENODEV;	}	portp->stats.state = portp->istate;	portp->stats.flags = portp->flags;	portp->stats.hwid = portp->hwid;	portp->stats.ttystate = 0;	portp->stats.cflags = 0;	portp->stats.iflags = 0;	portp->stats.oflags = 0;	portp->stats.lflags = 0;	portp

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?