Input: xilinx_ps2 - allocate serio port separately
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 26 Mar 2012 00:23:19 +0000 (17:23 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Tue, 3 Apr 2012 16:57:11 +0000 (09:57 -0700)
'struct serio' is a refcounted data structure with lifetime rules different
from 'struct xps2data'. It is quite likely that serio_unregister_port() will
try to free memory allocated by the port and that is why it should be
allocated separately.

Also switch to using platform_get/set_drvdata instead of dev_get/set_drvdata
because we are dealing with platform device.

Reported-by: Tobias Klauser <tklauser@distanz.ch>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/serio/xilinx_ps2.c

index d96d4c2a76a94793270c6e4ce6dda247b2e08ebe..1e983bec7d86c524243d55e57ff409e2d831571d 100644 (file)
@@ -73,7 +73,8 @@ struct xps2data {
        spinlock_t lock;
        void __iomem *base_address;     /* virt. address of control registers */
        unsigned int flags;
-       struct serio serio;             /* serio */
+       struct serio *serio;            /* serio */
+       struct device *dev;
 };
 
 /************************************/
@@ -119,7 +120,7 @@ static irqreturn_t xps2_interrupt(int irq, void *dev_id)
 
        /* Check which interrupt is active */
        if (intr_sr & XPS2_IPIXR_RX_OVF)
-               dev_warn(drvdata->serio.dev.parent, "receive overrun error\n");
+               dev_warn(drvdata->dev, "receive overrun error\n");
 
        if (intr_sr & XPS2_IPIXR_RX_ERR)
                drvdata->flags |= SERIO_PARITY;
@@ -132,10 +133,10 @@ static irqreturn_t xps2_interrupt(int irq, void *dev_id)
 
                /* Error, if a byte is not received */
                if (status) {
-                       dev_err(drvdata->serio.dev.parent,
+                       dev_err(drvdata->dev,
                                "wrong rcvd byte count (%d)\n", status);
                } else {
-                       serio_interrupt(&drvdata->serio, c, drvdata->flags);
+                       serio_interrupt(drvdata->serio, c, drvdata->flags);
                        drvdata->flags = 0;
                }
        }
@@ -193,7 +194,7 @@ static int sxps2_open(struct serio *pserio)
        error = request_irq(drvdata->irq, &xps2_interrupt, 0,
                                DRIVER_NAME, drvdata);
        if (error) {
-               dev_err(drvdata->serio.dev.parent,
+               dev_err(drvdata->dev,
                        "Couldn't allocate interrupt %d\n", drvdata->irq);
                return error;
        }
@@ -259,15 +260,16 @@ static int __devinit xps2_of_probe(struct platform_device *ofdev)
        }
 
        drvdata = kzalloc(sizeof(struct xps2data), GFP_KERNEL);
-       if (!drvdata) {
-               dev_err(dev, "Couldn't allocate device private record\n");
-               return -ENOMEM;
+       serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+       if (!drvdata || !serio) {
+               error = -ENOMEM;
+               goto failed1;
        }
 
-       dev_set_drvdata(dev, drvdata);
-
        spin_lock_init(&drvdata->lock);
        drvdata->irq = r_irq.start;
+       drvdata->serio = serio;
+       drvdata->dev = dev;
 
        phys_addr = r_mem.start;
        remap_size = resource_size(&r_mem);
@@ -298,7 +300,6 @@ static int __devinit xps2_of_probe(struct platform_device *ofdev)
                 (unsigned long long)phys_addr, drvdata->base_address,
                 drvdata->irq);
 
-       serio = &drvdata->serio;
        serio->id.type = SERIO_8042;
        serio->write = sxps2_write;
        serio->open = sxps2_open;
@@ -312,13 +313,14 @@ static int __devinit xps2_of_probe(struct platform_device *ofdev)
 
        serio_register_port(serio);
 
+       platform_set_drvdata(ofdev, drvdata);
        return 0;               /* success */
 
 failed2:
        release_mem_region(phys_addr, remap_size);
 failed1:
+       kfree(serio);
        kfree(drvdata);
-       dev_set_drvdata(dev, NULL);
 
        return error;
 }
@@ -333,22 +335,21 @@ failed1:
  */
 static int __devexit xps2_of_remove(struct platform_device *of_dev)
 {
-       struct device *dev = &of_dev->dev;
-       struct xps2data *drvdata = dev_get_drvdata(dev);
+       struct xps2data *drvdata = platform_get_drvdata(of_dev);
        struct resource r_mem; /* IO mem resources */
 
-       serio_unregister_port(&drvdata->serio);
+       serio_unregister_port(drvdata->serio);
        iounmap(drvdata->base_address);
 
        /* Get iospace of the device */
        if (of_address_to_resource(of_dev->dev.of_node, 0, &r_mem))
-               dev_err(dev, "invalid address\n");
+               dev_err(drvdata->dev, "invalid address\n");
        else
                release_mem_region(r_mem.start, resource_size(&r_mem));
 
        kfree(drvdata);
 
-       dev_set_drvdata(dev, NULL);
+       platform_set_drvdata(of_dev, NULL);
 
        return 0;
 }