Commit 3b85b341 authored by Jiri Slaby's avatar Jiri Slaby Committed by Linus Torvalds

[PATCH] Char: stallion, implement fail paths

This driver expect everything to work.  Implement fail paths logic to release
regions, irq hangler, memory...  if something is in bad state.
Signed-off-by: default avatarJiri Slaby <jirislaby@gmail.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent fefaf9a7
...@@ -779,7 +779,8 @@ static void __init stl_argbrds(void) ...@@ -779,7 +779,8 @@ static void __init stl_argbrds(void)
brdp->ioaddr2 = conf.ioaddr2; brdp->ioaddr2 = conf.ioaddr2;
brdp->irq = conf.irq; brdp->irq = conf.irq;
brdp->irqtype = conf.irqtype; brdp->irqtype = conf.irqtype;
stl_brdinit(brdp); if (stl_brdinit(brdp))
kfree(brdp);
} }
} }
...@@ -1965,6 +1966,29 @@ static int __init stl_initports(struct stlbrd *brdp, struct stlpanel *panelp) ...@@ -1965,6 +1966,29 @@ static int __init stl_initports(struct stlbrd *brdp, struct stlpanel *panelp)
return(0); 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);
}
}
/*****************************************************************************/ /*****************************************************************************/
/* /*
...@@ -1976,7 +2000,7 @@ static int __init stl_initeio(struct stlbrd *brdp) ...@@ -1976,7 +2000,7 @@ static int __init stl_initeio(struct stlbrd *brdp)
struct stlpanel *panelp; struct stlpanel *panelp;
unsigned int status; unsigned int status;
char *name; char *name;
int rc; int retval;
pr_debug("stl_initeio(brdp=%p)\n", brdp); pr_debug("stl_initeio(brdp=%p)\n", brdp);
...@@ -2003,18 +2027,20 @@ static int __init stl_initeio(struct stlbrd *brdp) ...@@ -2003,18 +2027,20 @@ static int __init stl_initeio(struct stlbrd *brdp)
(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
printk("STALLION: invalid irq=%d for brd=%d\n", printk("STALLION: invalid irq=%d for brd=%d\n",
brdp->irq, brdp->brdnr); brdp->irq, brdp->brdnr);
return(-EINVAL); retval = -EINVAL;
goto err;
} }
outb((stl_vecmap[brdp->irq] | EIO_0WS | outb((stl_vecmap[brdp->irq] | EIO_0WS |
((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)),
brdp->ioctrl); brdp->ioctrl);
} }
retval = -EBUSY;
if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) { if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) {
printk(KERN_WARNING "STALLION: Warning, board %d I/O address " printk(KERN_WARNING "STALLION: Warning, board %d I/O address "
"%x conflicts with another device\n", brdp->brdnr, "%x conflicts with another device\n", brdp->brdnr,
brdp->ioaddr1); brdp->ioaddr1);
return(-EBUSY); goto err;
} }
if (brdp->iosize2 > 0) if (brdp->iosize2 > 0)
...@@ -2025,8 +2051,7 @@ static int __init stl_initeio(struct stlbrd *brdp) ...@@ -2025,8 +2051,7 @@ static int __init stl_initeio(struct stlbrd *brdp)
printk(KERN_WARNING "STALLION: Warning, also " printk(KERN_WARNING "STALLION: Warning, also "
"releasing board %d I/O address %x \n", "releasing board %d I/O address %x \n",
brdp->brdnr, brdp->ioaddr1); brdp->brdnr, brdp->ioaddr1);
release_region(brdp->ioaddr1, brdp->iosize1); goto err_rel1;
return(-EBUSY);
} }
/* /*
...@@ -2035,6 +2060,7 @@ static int __init stl_initeio(struct stlbrd *brdp) ...@@ -2035,6 +2060,7 @@ static int __init stl_initeio(struct stlbrd *brdp)
brdp->clk = CD1400_CLK; brdp->clk = CD1400_CLK;
brdp->isr = stl_eiointr; brdp->isr = stl_eiointr;
retval = -ENODEV;
switch (status & EIO_IDBITMASK) { switch (status & EIO_IDBITMASK) {
case EIO_8PORTM: case EIO_8PORTM:
brdp->clk = CD1400_CLK8M; brdp->clk = CD1400_CLK8M;
...@@ -2058,11 +2084,11 @@ static int __init stl_initeio(struct stlbrd *brdp) ...@@ -2058,11 +2084,11 @@ static int __init stl_initeio(struct stlbrd *brdp)
brdp->nrports = 16; brdp->nrports = 16;
break; break;
default: default:
return(-ENODEV); goto err_rel2;
} }
break; break;
default: default:
return(-ENODEV); goto err_rel2;
} }
/* /*
...@@ -2074,7 +2100,8 @@ static int __init stl_initeio(struct stlbrd *brdp) ...@@ -2074,7 +2100,8 @@ static int __init stl_initeio(struct stlbrd *brdp)
if (!panelp) { if (!panelp) {
printk(KERN_WARNING "STALLION: failed to allocate memory " printk(KERN_WARNING "STALLION: failed to allocate memory "
"(size=%Zd)\n", sizeof(struct stlpanel)); "(size=%Zd)\n", sizeof(struct stlpanel));
return -ENOMEM; retval = -ENOMEM;
goto err_rel2;
} }
panelp->magic = STL_PANELMAGIC; panelp->magic = STL_PANELMAGIC;
...@@ -2098,11 +2125,20 @@ static int __init stl_initeio(struct stlbrd *brdp) ...@@ -2098,11 +2125,20 @@ static int __init stl_initeio(struct stlbrd *brdp)
if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) { if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) {
printk("STALLION: failed to register interrupt " printk("STALLION: failed to register interrupt "
"routine for %s irq=%d\n", name, brdp->irq); "routine for %s irq=%d\n", name, brdp->irq);
rc = -ENODEV; retval = -ENODEV;
} else { goto err_fr;
rc = 0;
} }
return rc;
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;
} }
/*****************************************************************************/ /*****************************************************************************/
...@@ -2116,7 +2152,7 @@ static int __init stl_initech(struct stlbrd *brdp) ...@@ -2116,7 +2152,7 @@ static int __init stl_initech(struct stlbrd *brdp)
{ {
struct stlpanel *panelp; struct stlpanel *panelp;
unsigned int status, nxtid, ioaddr, conflict; unsigned int status, nxtid, ioaddr, conflict;
int panelnr, banknr, i; int panelnr, banknr, i, retval;
char *name; char *name;
pr_debug("stl_initech(brdp=%p)\n", brdp); pr_debug("stl_initech(brdp=%p)\n", brdp);
...@@ -2136,13 +2172,16 @@ static int __init stl_initech(struct stlbrd *brdp) ...@@ -2136,13 +2172,16 @@ static int __init stl_initech(struct stlbrd *brdp)
brdp->ioctrl = brdp->ioaddr1 + 1; brdp->ioctrl = brdp->ioaddr1 + 1;
brdp->iostatus = brdp->ioaddr1 + 1; brdp->iostatus = brdp->ioaddr1 + 1;
status = inb(brdp->iostatus); status = inb(brdp->iostatus);
if ((status & ECH_IDBITMASK) != ECH_ID) if ((status & ECH_IDBITMASK) != ECH_ID) {
return(-ENODEV); retval = -ENODEV;
goto err;
}
if ((brdp->irq < 0) || (brdp->irq > 15) || if ((brdp->irq < 0) || (brdp->irq > 15) ||
(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
printk("STALLION: invalid irq=%d for brd=%d\n", printk("STALLION: invalid irq=%d for brd=%d\n",
brdp->irq, brdp->brdnr); brdp->irq, brdp->brdnr);
return(-EINVAL); retval = -EINVAL;
goto err;
} }
status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1); status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
status |= (stl_vecmap[brdp->irq] << 1); status |= (stl_vecmap[brdp->irq] << 1);
...@@ -2162,13 +2201,16 @@ static int __init stl_initech(struct stlbrd *brdp) ...@@ -2162,13 +2201,16 @@ static int __init stl_initech(struct stlbrd *brdp)
brdp->ioctrl = brdp->ioaddr1 + 0x20; brdp->ioctrl = brdp->ioaddr1 + 0x20;
brdp->iostatus = brdp->ioctrl; brdp->iostatus = brdp->ioctrl;
status = inb(brdp->iostatus); status = inb(brdp->iostatus);
if ((status & ECH_IDBITMASK) != ECH_ID) if ((status & ECH_IDBITMASK) != ECH_ID) {
return(-ENODEV); retval = -ENODEV;
goto err;
}
if ((brdp->irq < 0) || (brdp->irq > 15) || if ((brdp->irq < 0) || (brdp->irq > 15) ||
(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
printk("STALLION: invalid irq=%d for brd=%d\n", printk("STALLION: invalid irq=%d for brd=%d\n",
brdp->irq, brdp->brdnr); brdp->irq, brdp->brdnr);
return(-EINVAL); retval = -EINVAL;
goto err;
} }
outb(ECHMC_BRDRESET, brdp->ioctrl); outb(ECHMC_BRDRESET, brdp->ioctrl);
outb(ECHMC_INTENABLE, brdp->ioctrl); outb(ECHMC_INTENABLE, brdp->ioctrl);
...@@ -2195,19 +2237,20 @@ static int __init stl_initech(struct stlbrd *brdp) ...@@ -2195,19 +2237,20 @@ static int __init stl_initech(struct stlbrd *brdp)
default: default:
printk("STALLION: unknown board type=%d\n", brdp->brdtype); printk("STALLION: unknown board type=%d\n", brdp->brdtype);
return(-EINVAL); retval = -EINVAL;
break; goto err;
} }
/* /*
* Check boards for possible IO address conflicts and return fail status * Check boards for possible IO address conflicts and return fail status
* if an IO conflict found. * if an IO conflict found.
*/ */
retval = -EBUSY;
if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) { if (!request_region(brdp->ioaddr1, brdp->iosize1, name)) {
printk(KERN_WARNING "STALLION: Warning, board %d I/O address " printk(KERN_WARNING "STALLION: Warning, board %d I/O address "
"%x conflicts with another device\n", brdp->brdnr, "%x conflicts with another device\n", brdp->brdnr,
brdp->ioaddr1); brdp->ioaddr1);
return(-EBUSY); goto err;
} }
if (brdp->iosize2 > 0) if (brdp->iosize2 > 0)
...@@ -2218,8 +2261,7 @@ static int __init stl_initech(struct stlbrd *brdp) ...@@ -2218,8 +2261,7 @@ static int __init stl_initech(struct stlbrd *brdp)
printk(KERN_WARNING "STALLION: Warning, also " printk(KERN_WARNING "STALLION: Warning, also "
"releasing board %d I/O address %x \n", "releasing board %d I/O address %x \n",
brdp->brdnr, brdp->ioaddr1); brdp->brdnr, brdp->ioaddr1);
release_region(brdp->ioaddr1, brdp->iosize1); goto err_rel1;
return(-EBUSY);
} }
/* /*
...@@ -2241,12 +2283,12 @@ static int __init stl_initech(struct stlbrd *brdp) ...@@ -2241,12 +2283,12 @@ static int __init stl_initech(struct stlbrd *brdp)
} }
status = inb(ioaddr + ECH_PNLSTATUS); status = inb(ioaddr + ECH_PNLSTATUS);
if ((status & ECH_PNLIDMASK) != nxtid) if ((status & ECH_PNLIDMASK) != nxtid)
break; goto err_fr;
panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL); panelp = kzalloc(sizeof(struct stlpanel), GFP_KERNEL);
if (!panelp) { if (!panelp) {
printk("STALLION: failed to allocate memory " printk("STALLION: failed to allocate memory "
"(size=%Zd)\n", sizeof(struct stlpanel)); "(size=%Zd)\n", sizeof(struct stlpanel));
break; goto err_fr;
} }
panelp->magic = STL_PANELMAGIC; panelp->magic = STL_PANELMAGIC;
panelp->brdnr = brdp->brdnr; panelp->brdnr = brdp->brdnr;
...@@ -2294,7 +2336,7 @@ static int __init stl_initech(struct stlbrd *brdp) ...@@ -2294,7 +2336,7 @@ static int __init stl_initech(struct stlbrd *brdp)
brdp->panels[panelnr++] = panelp; brdp->panels[panelnr++] = panelp;
if ((brdp->brdtype != BRD_ECHPCI) && if ((brdp->brdtype != BRD_ECHPCI) &&
(ioaddr >= (brdp->ioaddr2 + brdp->iosize2))) (ioaddr >= (brdp->ioaddr2 + brdp->iosize2)))
break; goto err_fr;
} }
brdp->nrpanels = panelnr; brdp->nrpanels = panelnr;
...@@ -2306,12 +2348,19 @@ static int __init stl_initech(struct stlbrd *brdp) ...@@ -2306,12 +2348,19 @@ static int __init stl_initech(struct stlbrd *brdp)
if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) { if (request_irq(brdp->irq, stl_intr, IRQF_SHARED, name, brdp) != 0) {
printk("STALLION: failed to register interrupt " printk("STALLION: failed to register interrupt "
"routine for %s irq=%d\n", name, brdp->irq); "routine for %s irq=%d\n", name, brdp->irq);
i = -ENODEV; retval = -ENODEV;
} else { goto err_fr;
i = 0;
} }
return(i); 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;
} }
/*****************************************************************************/ /*****************************************************************************/
...@@ -2325,25 +2374,30 @@ static int __init stl_initech(struct stlbrd *brdp) ...@@ -2325,25 +2374,30 @@ static int __init stl_initech(struct stlbrd *brdp)
static int __init stl_brdinit(struct stlbrd *brdp) static int __init stl_brdinit(struct stlbrd *brdp)
{ {
int i; int i, retval;
pr_debug("stl_brdinit(brdp=%p)\n", brdp); pr_debug("stl_brdinit(brdp=%p)\n", brdp);
switch (brdp->brdtype) { switch (brdp->brdtype) {
case BRD_EASYIO: case BRD_EASYIO:
case BRD_EASYIOPCI: case BRD_EASYIOPCI:
stl_initeio(brdp); retval = stl_initeio(brdp);
if (retval)
goto err;
break; break;
case BRD_ECH: case BRD_ECH:
case BRD_ECHMC: case BRD_ECHMC:
case BRD_ECHPCI: case BRD_ECHPCI:
case BRD_ECH64PCI: case BRD_ECH64PCI:
stl_initech(brdp); retval = stl_initech(brdp);
if (retval)
goto err;
break; break;
default: default:
printk("STALLION: board=%d is unknown board type=%d\n", printk("STALLION: board=%d is unknown board type=%d\n",
brdp->brdnr, brdp->brdtype); brdp->brdnr, brdp->brdtype);
return(ENODEV); retval = -ENODEV;
goto err;
} }
stl_brds[brdp->brdnr] = brdp; stl_brds[brdp->brdnr] = brdp;
...@@ -2351,7 +2405,7 @@ static int __init stl_brdinit(struct stlbrd *brdp) ...@@ -2351,7 +2405,7 @@ static int __init stl_brdinit(struct stlbrd *brdp)
printk("STALLION: %s board not found, board=%d io=%x irq=%d\n", printk("STALLION: %s board not found, board=%d io=%x irq=%d\n",
stl_brdnames[brdp->brdtype], brdp->brdnr, stl_brdnames[brdp->brdtype], brdp->brdnr,
brdp->ioaddr1, brdp->irq); brdp->ioaddr1, brdp->irq);
return(ENODEV); goto err_free;
} }
for (i = 0; (i < STL_MAXPANELS); i++) for (i = 0; (i < STL_MAXPANELS); i++)
...@@ -2362,7 +2416,20 @@ static int __init stl_brdinit(struct stlbrd *brdp) ...@@ -2362,7 +2416,20 @@ static int __init stl_brdinit(struct stlbrd *brdp)
"nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], "nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype],
brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels,
brdp->nrports); brdp->nrports);
return(0);
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);
stl_brds[brdp->brdnr] = NULL;
err:
return retval;
} }
/*****************************************************************************/ /*****************************************************************************/
...@@ -2385,29 +2452,6 @@ static int __init stl_getbrdnr(void) ...@@ -2385,29 +2452,6 @@ static int __init stl_getbrdnr(void)
return(-1); return(-1);
} }
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);
}
}
/*****************************************************************************/ /*****************************************************************************/
/* /*
* We have a Stallion board. Allocate a board structure and * We have a Stallion board. Allocate a board structure and
...@@ -2420,21 +2464,27 @@ static int __devinit stl_pciprobe(struct pci_dev *pdev, ...@@ -2420,21 +2464,27 @@ static int __devinit stl_pciprobe(struct pci_dev *pdev,
{ {
struct stlbrd *brdp; struct stlbrd *brdp;
unsigned int brdtype = ent->driver_data; unsigned int brdtype = ent->driver_data;
int retval = -ENODEV;
if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE)
return -ENODEV; goto err;
dev_info(&pdev->dev, "please, report this to LKML: %x/%x/%x\n", dev_info(&pdev->dev, "please, report this to LKML: %x/%x/%x\n",
pdev->vendor, pdev->device, pdev->class); pdev->vendor, pdev->device, pdev->class);
if (pci_enable_device(pdev)) retval = pci_enable_device(pdev);
return(-EIO); if (retval)
if ((brdp = stl_allocbrd()) == NULL) goto err;
return(-ENOMEM); brdp = stl_allocbrd();
if ((brdp->brdnr = stl_getbrdnr()) < 0) { if (brdp == NULL) {
retval = -ENOMEM;
goto err;
}
brdp->brdnr = stl_getbrdnr();
if (brdp->brdnr < 0) {
dev_err(&pdev->dev, "too many boards found, " dev_err(&pdev->dev, "too many boards found, "
"maximum supported %d\n", STL_MAXBRDS); "maximum supported %d\n", STL_MAXBRDS);
return(0); goto err_fr;
} }
brdp->brdtype = brdtype; brdp->brdtype = brdtype;
...@@ -2461,11 +2511,17 @@ static int __devinit stl_pciprobe(struct pci_dev *pdev, ...@@ -2461,11 +2511,17 @@ static int __devinit stl_pciprobe(struct pci_dev *pdev,
} }
brdp->irq = pdev->irq; brdp->irq = pdev->irq;
stl_brdinit(brdp); retval = stl_brdinit(brdp);
if (retval)
goto err_fr;
pci_set_drvdata(pdev, brdp); pci_set_drvdata(pdev, brdp);
return(0); return 0;
err_fr:
kfree(brdp);
err:
return retval;
} }
static void __devexit stl_pciremove(struct pci_dev *pdev) static void __devexit stl_pciremove(struct pci_dev *pdev)
...@@ -2528,7 +2584,8 @@ static int __init stl_initbrds(void) ...@@ -2528,7 +2584,8 @@ static int __init stl_initbrds(void)
brdp->ioaddr2 = confp->ioaddr2; brdp->ioaddr2 = confp->ioaddr2;
brdp->irq = confp->irq; brdp->irq = confp->irq;
brdp->irqtype = confp->irqtype; brdp->irqtype = confp->irqtype;
stl_brdinit(brdp); if (stl_brdinit(brdp))
kfree(brdp);
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment