Interface management.
authorFlorian Pose <fp@igh-essen.com>
Tue, 01 Dec 2009 21:45:45 +0100
changeset 1569 61f5f4c35883
parent 1568 d9bbbe1883ab
child 1570 7a2a58107dd8
Interface management.
include/ectty.h
tty/module.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/ectty.h	Tue Dec 01 21:45:45 2009 +0100
@@ -0,0 +1,76 @@
+/******************************************************************************
+ *
+ *  $Id$
+ *
+ *  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ *  This file is part of the IgH EtherCAT master userspace library.
+ *  
+ *  The IgH EtherCAT master userspace library is free software; you can
+ *  redistribute it and/or modify it under the terms of the GNU Lesser General
+ *  Public License as published by the Free Software Foundation; version 2.1
+ *  of the License.
+ *
+ *  The IgH EtherCAT master userspace library is distributed in the hope that
+ *  it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with the IgH EtherCAT master userspace library. If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *  
+ *  ---
+ *  
+ *  The license mentioned above concerns the source code only. Using the
+ *  EtherCAT technology and brand is only permitted in compliance with the
+ *  industrial property and similar rights of Beckhoff Automation GmbH.
+ *
+ *****************************************************************************/
+
+/** \file
+ *
+ * EtherCAT virtual TTY interface.
+ *
+ * \defgroup TTYInterface EtherCAT Virtual TTY Interface
+ *
+ * @{
+ */
+
+/*****************************************************************************/
+
+#ifndef __ECTTY_H__
+#define __ECTTY_H__
+
+/******************************************************************************
+ * Data types 
+ *****************************************************************************/
+
+struct ec_tty;
+typedef struct ec_tty ec_tty_t; /**< \see ec_tty */
+
+/******************************************************************************
+ * Global functions
+ *****************************************************************************/
+
+/** Create a virtual TTY interface.
+ * 
+ * \return Pointer to the interface object, otherwise an ERR_PTR value.
+ */
+ec_tty_t *ectty_create(void);
+
+/******************************************************************************
+ * TTY interface methods
+ *****************************************************************************/
+
+/** Releases a virtual TTY interface.
+ */
+void ectty_free(
+        ec_tty_t *tty /**< TTY interface. */
+        );
+
+/*****************************************************************************/
+
+/** @} */
+
+#endif
--- a/tty/module.c	Tue Dec 01 17:35:33 2009 +0100
+++ b/tty/module.c	Tue Dec 01 21:45:45 2009 +0100
@@ -38,17 +38,23 @@
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/termios.h>
+#include <linux/semaphore.h>
 
 #include "../master/globals.h"
-
-/*****************************************************************************/
-
-int __init ec_tty_init_module(void);
-void __exit ec_tty_cleanup_module(void);
-
-unsigned int debug_level = 0;
+#include "../include/ectty.h"
+
+/*****************************************************************************/
+
+#define EC_TTY_MAX_DEVICES 10
+
+/*****************************************************************************/
+
 char *ec_master_version_str = EC_MASTER_VERSION; /**< Version string. */
 
+static struct tty_driver *tty_driver = NULL;
+ec_tty_t *ttys[EC_TTY_MAX_DEVICES];
+struct semaphore tty_sem;
+
 /*****************************************************************************/
 
 /** \cond */
@@ -58,11 +64,11 @@
 MODULE_LICENSE("GPL");
 MODULE_VERSION(EC_MASTER_VERSION);
 
+unsigned int debug_level = 0;
 module_param_named(debug_level, debug_level, uint, S_IRUGO);
 MODULE_PARM_DESC(debug_level, "Debug level");
 
-struct tty_driver *tty_driver = NULL;
-struct device *tty_device = NULL;
+/** \endcond */
 
 static int ec_tty_open(struct tty_struct *, struct file *);
 static void ec_tty_close(struct tty_struct *, struct file *);
@@ -82,7 +88,10 @@
     .c_cc = INIT_C_CC,
 };
 
-/** \endcond */
+struct ec_tty {
+    int minor;
+    struct device *dev;
+};
 
 /*****************************************************************************/
 
@@ -92,11 +101,17 @@
  */
 int __init ec_tty_init_module(void)
 {
-    int ret = 0;
+    int i, ret = 0;
 
     EC_INFO("TTY driver %s\n", EC_MASTER_VERSION);
 
-    tty_driver = alloc_tty_driver(1);
+    init_MUTEX(&tty_sem);
+
+    for (i = 0; i < EC_TTY_MAX_DEVICES; i++) {
+        ttys[i] = NULL;
+    }
+
+    tty_driver = alloc_tty_driver(EC_TTY_MAX_DEVICES);
     if (!tty_driver) {
         EC_ERR("Failed to allocate tty driver.\n");
         ret = -ENOMEM;
@@ -121,17 +136,8 @@
         goto out_put;
     }
 
-    tty_device = tty_register_device(tty_driver, 0, NULL);
-    if (IS_ERR(tty_device)) {
-        EC_ERR("Failed to register tty device.\n");
-        ret = PTR_ERR(tty_device);
-        goto out_unreg;
-    }
-
     return ret;
         
-out_unreg:
-    tty_unregister_driver(tty_driver);
 out_put:
     put_tty_driver(tty_driver);
 out_return:
@@ -146,7 +152,6 @@
  */
 void __exit ec_tty_cleanup_module(void)
 {
-    tty_unregister_device(tty_driver, 0);
     tty_unregister_driver(tty_driver);
     put_tty_driver(tty_driver);
     EC_INFO("TTY module cleaned up.\n");
@@ -154,6 +159,30 @@
 
 /*****************************************************************************/
 
+int ec_tty_init(ec_tty_t *tty, int minor)
+{
+    tty->minor = minor;
+
+    tty->dev = tty_register_device(tty_driver, tty->minor, NULL);
+    if (IS_ERR(tty->dev)) {
+        EC_ERR("Failed to register tty device.\n");
+        return PTR_ERR(tty->dev);
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+void ec_tty_clear(ec_tty_t *tty)
+{
+    tty_unregister_device(tty_driver, tty->minor);
+}
+
+/******************************************************************************
+ * Device callbacks
+ *****************************************************************************/
+
 static int ec_tty_open(struct tty_struct *tty, struct file *file)
 {
     return -EBUSY;
@@ -177,6 +206,55 @@
     return -EIO;
 }
 
+/******************************************************************************
+ * Public functions and methods
+ *****************************************************************************/
+
+ec_tty_t *ectty_create(void)
+{
+    ec_tty_t *tty;
+    int minor, ret;
+
+    if (down_interruptible(&tty_sem)) {
+        return ERR_PTR(-EINTR);
+    }
+
+    for (minor = 0; minor < EC_TTY_MAX_DEVICES; minor++) {
+        if (!ttys[minor]) {
+            tty = kmalloc(sizeof(ec_tty_t), GFP_KERNEL);
+            if (!tty) {
+                up(&tty_sem);
+                EC_ERR("Failed to allocate memory for tty device.\n");
+                return ERR_PTR(-ENOMEM);
+            }
+
+            ret = ec_tty_init(tty, minor);
+            if (ret) {
+                up(&tty_sem);
+                kfree(tty);
+                return ERR_PTR(ret);
+            }
+
+            ttys[minor] = tty;
+            up(&tty_sem);
+            return tty;
+        }
+    }
+
+    up(&tty_sem);
+    return ERR_PTR(-EBUSY);
+}
+
+/*****************************************************************************/
+
+void ectty_free(ec_tty_t *tty)
+{
+    int minor = tty->minor;
+    ec_tty_clear(tty);
+    ttys[minor] = NULL;
+    kfree(tty);
+}
+
 /*****************************************************************************/
 
 /** \cond */
@@ -184,6 +262,9 @@
 module_init(ec_tty_init_module);
 module_exit(ec_tty_cleanup_module);
 
+EXPORT_SYMBOL(ectty_create);
+EXPORT_SYMBOL(ectty_free);
+
 /** \endcond */
 
 /*****************************************************************************/