From ff45c265f849ce42e8d1eb48e49e8176019ec5af Mon Sep 17 00:00:00 2001
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Date: Fri, 15 Aug 2014 18:33:33 +0800
Subject: [PATCH] greybus: uart-gb: more work on tty functions

---
 drivers/staging/greybus/uart-gb.c | 86 ++++++++++++++++++++++++++++++-
 1 file changed, 84 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/greybus/uart-gb.c b/drivers/staging/greybus/uart-gb.c
index 95273073a4af2..3eaada99951fc 100644
--- a/drivers/staging/greybus/uart-gb.c
+++ b/drivers/staging/greybus/uart-gb.c
@@ -13,6 +13,7 @@
 #include <linux/serial.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
+#include <linux/idr.h>
 #include "greybus.h"
 
 #define GB_TTY_MAJOR 180	/* FIXME use a real number!!! */
@@ -31,6 +32,89 @@ static const struct greybus_device_id id_table[] = {
 	{ },	/* terminating NULL entry */
 };
 
+static struct tty_driver *gb_tty_driver;
+static DEFINE_IDR(tty_minors);
+static DEFINE_MUTEX(table_lock);
+
+static struct gb_tty *get_gb_by_minor(unsigned minor)
+{
+	struct gb_tty *gb_tty;
+
+	mutex_lock(&table_lock);
+	gb_tty = idr_find(&tty_minors, minor);
+	mutex_unlock(&table_lock);
+	return gb_tty;
+}
+
+static int alloc_minor(struct gb_tty *gb_tty)
+{
+	int minor;
+
+	mutex_lock(&table_lock);
+	minor = idr_alloc(&tty_minors, gb_tty, 0, 0, GFP_KERNEL);
+	if (minor < 0)
+		goto error;
+	gb_tty->minor = minor;
+error:
+	mutex_unlock(&table_lock);
+	return minor;
+}
+
+static void release_minor(struct gb_tty *gb_tty)
+{
+	mutex_lock(&table_lock);
+	idr_remove(&tty_minors, gb_tty->minor);
+	mutex_unlock(&table_lock);
+}
+
+static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	struct gb_tty *gb_tty;
+	int retval;
+
+	gb_tty = get_gb_by_minor(tty->index);
+	if (!gb_tty)
+		return -ENODEV;
+
+	retval = tty_standard_install(driver, tty);
+	if (retval)
+		goto error;
+
+	tty->driver_data = gb_tty;
+	return 0;
+error:
+	tty_port_put(&gb_tty->port);
+	return retval;
+}
+
+static int gb_tty_open(struct tty_struct *tty, struct file *file)
+{
+	struct gb_tty *gb_tty = tty->driver_data;
+
+	return tty_port_open(&gb_tty->port, tty, file);
+}
+
+static void gb_tty_close(struct tty_struct *tty, struct file *file)
+{
+	struct gb_tty *gb_tty = tty->driver_data;
+
+	tty_port_close(&gb_tty->port, tty, file);
+}
+
+static void gb_tty_cleanup(struct tty_struct *tty)
+{
+	struct gb_tty *gb_tty = tty->driver_data;
+
+	tty_port_put(&gb_tty->port);
+}
+
+static void gb_tty_hangup(struct tty_struct *tty)
+{
+	struct gb_tty *gb_tty = tty->driver_data;
+
+	tty_port_hangup(&gb_tty->port);
+}
+
 
 static const struct tty_operations gb_ops = {
 	.install =		gb_tty_install,
@@ -50,8 +134,6 @@ static const struct tty_operations gb_ops = {
 	.tiocmset =		gb_tty_tiocmset,
 };
 
-static struct tty_driver *gb_tty_driver;
-static struct gb_tty *gb_tty_table[255];
 
 static int tty_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id)
 {
-- 
2.30.2