]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/char/hvc_console.c
Merge branches 'release', 'acpi_pm_device_sleep_state' and 'battery' into release
[linux-2.6-omap-h63xx.git] / drivers / char / hvc_console.c
index 322bc5f7d86b791dd8350642c1b390fe6b54784c..44160d5ebca06dbcd7e300c9835cbeb1763c1a4c 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/init.h>
 #include <linux/kbd_kern.h>
 #include <linux/kernel.h>
-#include <linux/kobject.h>
+#include <linux/kref.h>
 #include <linux/kthread.h>
 #include <linux/list.h>
 #include <linux/module.h>
@@ -69,6 +69,8 @@ static struct task_struct *hvc_task;
 /* Picks up late kicks after list walk but before schedule() */
 static int hvc_kicked;
 
+static int hvc_init(void);
+
 #ifdef CONFIG_MAGIC_SYSRQ
 static int sysrq_pressed;
 #endif
@@ -87,11 +89,11 @@ struct hvc_struct {
        int irq_requested;
        int irq;
        struct list_head next;
-       struct kobject kobj; /* ref count & hvc_struct lifetime */
+       struct kref kref; /* ref count & hvc_struct lifetime */
 };
 
 /* dynamic list of hvc_struct instances */
-static struct list_head hvc_structs = LIST_HEAD_INIT(hvc_structs);
+static LIST_HEAD(hvc_structs);
 
 /*
  * Protect the list of hvc_struct instances from inserts and removals during
@@ -108,7 +110,7 @@ static int last_hvc = -1;
 
 /*
  * Do not call this function with either the hvc_structs_lock or the hvc_struct
- * lock held.  If successful, this function increments the kobject reference
+ * lock held.  If successful, this function increments the kref reference
  * count against the target hvc_struct so it should be released when finished.
  */
 static struct hvc_struct *hvc_get_by_index(int index)
@@ -121,7 +123,7 @@ static struct hvc_struct *hvc_get_by_index(int index)
        list_for_each_entry(hp, &hvc_structs, next) {
                spin_lock_irqsave(&hp->lock, flags);
                if (hp->index == index) {
-                       kobject_get(&hp->kobj);
+                       kref_get(&hp->kref);
                        spin_unlock_irqrestore(&hp->lock, flags);
                        spin_unlock(&hvc_structs_lock);
                        return hp;
@@ -240,6 +242,23 @@ static int __init hvc_console_init(void)
 }
 console_initcall(hvc_console_init);
 
+/* callback when the kboject ref count reaches zero. */
+static void destroy_hvc_struct(struct kref *kref)
+{
+       struct hvc_struct *hp = container_of(kref, struct hvc_struct, kref);
+       unsigned long flags;
+
+       spin_lock(&hvc_structs_lock);
+
+       spin_lock_irqsave(&hp->lock, flags);
+       list_del(&(hp->next));
+       spin_unlock_irqrestore(&hp->lock, flags);
+
+       spin_unlock(&hvc_structs_lock);
+
+       kfree(hp);
+}
+
 /*
  * hvc_instantiate() is an early console discovery method which locates
  * consoles * prior to the vio subsystem discovering them.  Hotplugged
@@ -259,7 +278,7 @@ int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops)
        /* make sure no no tty has been registered in this index */
        hp = hvc_get_by_index(index);
        if (hp) {
-               kobject_put(&hp->kobj);
+               kref_put(&hp->kref, destroy_hvc_struct);
                return -1;
        }
 
@@ -316,9 +335,8 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
        unsigned long flags;
        int irq = 0;
        int rc = 0;
-       struct kobject *kobjp;
 
-       /* Auto increments kobject reference if found. */
+       /* Auto increments kref reference if found. */
        if (!(hp = hvc_get_by_index(tty->index)))
                return -ENODEV;
 
@@ -339,8 +357,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
        if (irq)
                hp->irq_requested = 1;
 
-       kobjp = &hp->kobj;
-
        spin_unlock_irqrestore(&hp->lock, flags);
        /* check error, fallback to non-irq */
        if (irq)
@@ -350,7 +366,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
         * If the request_irq() fails and we return an error.  The tty layer
         * will call hvc_close() after a failed open but we don't want to clean
         * up there so we'll clean up here and clear out the previously set
-        * tty fields and return the kobject reference.
+        * tty fields and return the kref reference.
         */
        if (rc) {
                spin_lock_irqsave(&hp->lock, flags);
@@ -358,7 +374,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
                hp->irq_requested = 0;
                spin_unlock_irqrestore(&hp->lock, flags);
                tty->driver_data = NULL;
-               kobject_put(kobjp);
+               kref_put(&hp->kref, destroy_hvc_struct);
                printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc);
        }
        /* Force wakeup of the polling thread */
@@ -370,7 +386,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
 static void hvc_close(struct tty_struct *tty, struct file * filp)
 {
        struct hvc_struct *hp;
-       struct kobject *kobjp;
        int irq = 0;
        unsigned long flags;
 
@@ -380,7 +395,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
        /*
         * No driver_data means that this close was issued after a failed
         * hvc_open by the tty layer's release_dev() function and we can just
-        * exit cleanly because the kobject reference wasn't made.
+        * exit cleanly because the kref reference wasn't made.
         */
        if (!tty->driver_data)
                return;
@@ -388,7 +403,6 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
        hp = tty->driver_data;
        spin_lock_irqsave(&hp->lock, flags);
 
-       kobjp = &hp->kobj;
        if (--hp->count == 0) {
                if (hp->irq_requested)
                        irq = hp->irq;
@@ -415,7 +429,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
                spin_unlock_irqrestore(&hp->lock, flags);
        }
 
-       kobject_put(kobjp);
+       kref_put(&hp->kref, destroy_hvc_struct);
 }
 
 static void hvc_hangup(struct tty_struct *tty)
@@ -424,7 +438,6 @@ static void hvc_hangup(struct tty_struct *tty)
        unsigned long flags;
        int irq = 0;
        int temp_open_count;
-       struct kobject *kobjp;
 
        if (!hp)
                return;
@@ -441,7 +454,6 @@ static void hvc_hangup(struct tty_struct *tty)
                return;
        }
 
-       kobjp = &hp->kobj;
        temp_open_count = hp->count;
        hp->count = 0;
        hp->n_outbuf = 0;
@@ -455,7 +467,7 @@ static void hvc_hangup(struct tty_struct *tty)
                free_irq(irq, hp);
        while(temp_open_count) {
                --temp_open_count;
-               kobject_put(kobjp);
+               kref_put(&hp->kref, destroy_hvc_struct);
        }
 }
 
@@ -674,11 +686,12 @@ static const cpumask_t cpus_in_xmon = CPU_MASK_NONE;
  * calling hvc_poll() who determines whether a console adapter support
  * interrupts.
  */
-int khvcd(void *unused)
+static int khvcd(void *unused)
 {
        int poll_mask;
        struct hvc_struct *hp;
 
+       set_freezable();
        __set_current_state(TASK_RUNNING);
        do {
                poll_mask = 0;
@@ -726,33 +739,19 @@ static const struct tty_operations hvc_ops = {
        .chars_in_buffer = hvc_chars_in_buffer,
 };
 
-/* callback when the kboject ref count reaches zero. */
-static void destroy_hvc_struct(struct kobject *kobj)
-{
-       struct hvc_struct *hp = container_of(kobj, struct hvc_struct, kobj);
-       unsigned long flags;
-
-       spin_lock(&hvc_structs_lock);
-
-       spin_lock_irqsave(&hp->lock, flags);
-       list_del(&(hp->next));
-       spin_unlock_irqrestore(&hp->lock, flags);
-
-       spin_unlock(&hvc_structs_lock);
-
-       kfree(hp);
-}
-
-static struct kobj_type hvc_kobj_type = {
-       .release = destroy_hvc_struct,
-};
-
 struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
                                        struct hv_ops *ops, int outbuf_size)
 {
        struct hvc_struct *hp;
        int i;
 
+       /* We wait until a driver actually comes along */
+       if (!hvc_driver) {
+               int err = hvc_init();
+               if (err)
+                       return ERR_PTR(err);
+       }
+
        hp = kmalloc(ALIGN(sizeof(*hp), sizeof(long)) + outbuf_size,
                        GFP_KERNEL);
        if (!hp)
@@ -766,8 +765,7 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
        hp->outbuf_size = outbuf_size;
        hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))];
 
-       kobject_init(&hp->kobj);
-       hp->kobj.ktype = &hvc_kobj_type;
+       kref_init(&hp->kref);
 
        spin_lock_init(&hp->lock);
        spin_lock(&hvc_structs_lock);
@@ -796,12 +794,10 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
 int __devexit hvc_remove(struct hvc_struct *hp)
 {
        unsigned long flags;
-       struct kobject *kobjp;
        struct tty_struct *tty;
 
        spin_lock_irqsave(&hp->lock, flags);
        tty = hp->tty;
-       kobjp = &hp->kobj;
 
        if (hp->index < MAX_NR_HVC_CONSOLES)
                vtermnos[hp->index] = -1;
@@ -811,12 +807,12 @@ int __devexit hvc_remove(struct hvc_struct *hp)
        spin_unlock_irqrestore(&hp->lock, flags);
 
        /*
-        * We 'put' the instance that was grabbed when the kobject instance
-        * was initialized using kobject_init().  Let the last holder of this
-        * kobject cause it to be removed, which will probably be the tty_hangup
+        * We 'put' the instance that was grabbed when the kref instance
+        * was initialized using kref_init().  Let the last holder of this
+        * kref cause it to be removed, which will probably be the tty_hangup
         * below.
         */
-       kobject_put(kobjp);
+       kref_put(&hp->kref, destroy_hvc_struct);
 
        /*
         * This function call will auto chain call hvc_hangup.  The tty should
@@ -828,16 +824,18 @@ int __devexit hvc_remove(struct hvc_struct *hp)
        return 0;
 }
 
-/* Driver initialization.  Follow console initialization.  This is where the TTY
- * interfaces start to become available. */
-static int __init hvc_init(void)
+/* Driver initialization: called as soon as someone uses hvc_alloc(). */
+static int hvc_init(void)
 {
        struct tty_driver *drv;
+       int err;
 
        /* We need more than hvc_count adapters due to hotplug additions. */
        drv = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS);
-       if (!drv)
-               return -ENOMEM;
+       if (!drv) {
+               err = -ENOMEM;
+               goto out;
+       }
 
        drv->owner = THIS_MODULE;
        drv->driver_name = "hvc";
@@ -853,30 +851,43 @@ static int __init hvc_init(void)
         * added later. */
        hvc_task = kthread_run(khvcd, NULL, "khvcd");
        if (IS_ERR(hvc_task)) {
-               panic("Couldn't create kthread for console.\n");
-               put_tty_driver(drv);
-               return -EIO;
+               printk(KERN_ERR "Couldn't create kthread for console.\n");
+               err = PTR_ERR(hvc_task);
+               goto put_tty;
        }
 
-       if (tty_register_driver(drv))
-               panic("Couldn't register hvc console driver\n");
+       err = tty_register_driver(drv);
+       if (err) {
+               printk(KERN_ERR "Couldn't register hvc console driver\n");
+               goto stop_thread;
+       }
 
+       /* FIXME: This mb() seems completely random.  Remove it. */
        mb();
        hvc_driver = drv;
        return 0;
+
+put_tty:
+       put_tty_driver(hvc_driver);
+stop_thread:
+       kthread_stop(hvc_task);
+       hvc_task = NULL;
+out:
+       return err;
 }
-module_init(hvc_init);
 
 /* This isn't particularly necessary due to this being a console driver
  * but it is nice to be thorough.
  */
 static void __exit hvc_exit(void)
 {
-       kthread_stop(hvc_task);
+       if (hvc_driver) {
+               kthread_stop(hvc_task);
 
-       tty_unregister_driver(hvc_driver);
-       /* return tty_struct instances allocated in hvc_init(). */
-       put_tty_driver(hvc_driver);
-       unregister_console(&hvc_con_driver);
+               tty_unregister_driver(hvc_driver);
+               /* return tty_struct instances allocated in hvc_init(). */
+               put_tty_driver(hvc_driver);
+               unregister_console(&hvc_con_driver);
+       }
 }
 module_exit(hvc_exit);