fp@1565: /****************************************************************************** fp@1565: * fp@1565: * $Id$ fp@1565: * fp@1565: * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH fp@1565: * fp@1565: * This file is part of the IgH EtherCAT Master. fp@1565: * fp@1565: * The IgH EtherCAT Master is free software; you can redistribute it and/or fp@1565: * modify it under the terms of the GNU General Public License version 2, as fp@1565: * published by the Free Software Foundation. fp@1565: * fp@1565: * The IgH EtherCAT Master is distributed in the hope that it will be useful, fp@1565: * but WITHOUT ANY WARRANTY; without even the implied warranty of fp@1565: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General fp@1565: * Public License for more details. fp@1565: * fp@1565: * You should have received a copy of the GNU General Public License along fp@1565: * with the IgH EtherCAT Master; if not, write to the Free Software fp@1565: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@1565: * fp@1565: * --- fp@1565: * fp@1565: * The license mentioned above concerns the source code only. Using the fp@1565: * EtherCAT technology and brand is only permitted in compliance with the fp@1565: * industrial property and similar rights of Beckhoff Automation GmbH. fp@1565: * fp@1565: *****************************************************************************/ fp@1565: fp@1565: /** \file fp@1565: * EtherCAT tty driver module. fp@1565: */ fp@1565: fp@1565: /*****************************************************************************/ fp@1565: fp@1565: #include fp@1565: #include fp@1568: #include fp@1568: #include fp@1577: #include fp@1568: #include fp@1575: #include fp@1595: #include fp@1778: #include fp@1778: #include fp@1565: fp@1565: #include "../master/globals.h" fp@1569: #include "../include/ectty.h" fp@1569: fp@1569: /*****************************************************************************/ fp@1569: fp@1570: #define PFX "ec_tty: " fp@1570: fp@1569: #define EC_TTY_MAX_DEVICES 10 fp@1575: #define EC_TTY_TX_BUFFER_SIZE 100 fp@1577: #define EC_TTY_RX_BUFFER_SIZE 100 fp@1577: fp@1577: #define EC_TTY_DEBUG 0 fp@1569: fp@1569: /*****************************************************************************/ fp@1569: fp@1565: char *ec_master_version_str = EC_MASTER_VERSION; /**< Version string. */ fp@1570: unsigned int debug_level = 0; fp@1565: fp@1569: static struct tty_driver *tty_driver = NULL; fp@1569: ec_tty_t *ttys[EC_TTY_MAX_DEVICES]; fp@1569: struct semaphore tty_sem; fp@1569: fp@1786: void ec_tty_wakeup(unsigned long); fp@1786: fp@1565: /*****************************************************************************/ fp@1565: fp@1565: /** \cond */ fp@1565: fp@1565: MODULE_AUTHOR("Florian Pose "); fp@1565: MODULE_DESCRIPTION("EtherCAT TTY driver module"); fp@1565: MODULE_LICENSE("GPL"); fp@1565: MODULE_VERSION(EC_MASTER_VERSION); fp@1565: fp@1565: module_param_named(debug_level, debug_level, uint, S_IRUGO); fp@1565: MODULE_PARM_DESC(debug_level, "Debug level"); fp@1565: fp@1569: /** \endcond */ fp@1568: fp@1778: /** Standard termios for ec_tty devices. fp@1778: * fp@1778: * Simplest possible configuration, as you would expect. fp@1778: */ fp@1568: static struct ktermios ec_tty_std_termios = { fp@1778: .c_iflag = 0, fp@1778: .c_oflag = 0, fp@1778: .c_cflag = B9600 | CS8 | CREAD, fp@1778: .c_lflag = 0, fp@1568: .c_cc = INIT_C_CC, fp@1568: }; fp@1568: fp@1569: struct ec_tty { fp@1569: int minor; fp@1569: struct device *dev; fp@1577: fp@1575: uint8_t tx_buffer[EC_TTY_TX_BUFFER_SIZE]; fp@1575: unsigned int tx_read_idx; fp@1575: unsigned int tx_write_idx; fp@1575: unsigned int wakeup; fp@1577: fp@1577: uint8_t rx_buffer[EC_TTY_RX_BUFFER_SIZE]; fp@1577: unsigned int rx_read_idx; fp@1577: unsigned int rx_write_idx; fp@1577: fp@1575: struct timer_list timer; fp@1575: struct tty_struct *tty; fp@1779: fp@1787: ec_tty_operations_t ops; fp@1779: void *cb_data; fp@1569: }; fp@1565: fp@1575: static const struct tty_operations ec_tty_ops; // see below fp@1575: fp@1565: /*****************************************************************************/ fp@1565: fp@1565: /** Module initialization. fp@1565: * fp@1565: * \return 0 on success, else < 0 fp@1565: */ fp@1565: int __init ec_tty_init_module(void) fp@1565: { fp@1569: int i, ret = 0; fp@1565: fp@1570: printk(KERN_INFO PFX "TTY driver %s\n", EC_MASTER_VERSION); fp@1565: fp@1569: init_MUTEX(&tty_sem); fp@1569: fp@1569: for (i = 0; i < EC_TTY_MAX_DEVICES; i++) { fp@1569: ttys[i] = NULL; fp@1569: } fp@1569: fp@1569: tty_driver = alloc_tty_driver(EC_TTY_MAX_DEVICES); fp@1568: if (!tty_driver) { fp@1570: printk(KERN_ERR PFX "Failed to allocate tty driver.\n"); fp@1568: ret = -ENOMEM; fp@1568: goto out_return; fp@1568: } fp@1568: fp@1568: tty_driver->owner = THIS_MODULE; fp@1568: tty_driver->driver_name = "EtherCAT TTY"; fp@1568: tty_driver->name = "ttyEC"; fp@1568: tty_driver->major = 0; fp@1568: tty_driver->minor_start = 0; fp@1568: tty_driver->type = TTY_DRIVER_TYPE_SERIAL; fp@1568: tty_driver->subtype = SERIAL_TYPE_NORMAL; fp@1568: tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; fp@1568: tty_driver->init_termios = ec_tty_std_termios; fp@1568: tty_set_operations(tty_driver, &ec_tty_ops); fp@1568: fp@1568: ret = tty_register_driver(tty_driver); fp@1568: if (ret) { fp@1570: printk(KERN_ERR PFX "Failed to register tty driver.\n"); fp@1568: goto out_put; fp@1568: } fp@1568: fp@1568: return ret; fp@1568: fp@1568: out_put: fp@1568: put_tty_driver(tty_driver); fp@1568: out_return: fp@1565: return ret; fp@1565: } fp@1565: fp@1565: /*****************************************************************************/ fp@1565: fp@1565: /** Module cleanup. fp@1565: * fp@1565: * Clears all master instances. fp@1565: */ fp@1565: void __exit ec_tty_cleanup_module(void) fp@1565: { fp@1568: tty_unregister_driver(tty_driver); fp@1568: put_tty_driver(tty_driver); fp@1570: printk(KERN_INFO PFX "Module unloading.\n"); fp@1565: } fp@1565: fp@1786: /****************************************************************************** fp@1786: * ec_tty_t methods. fp@1786: *****************************************************************************/ fp@1577: fp@1779: int ec_tty_init(ec_tty_t *tty, int minor, fp@1787: const ec_tty_operations_t *ops, void *cb_data) fp@1577: { fp@1577: tty->minor = minor; fp@1577: tty->tx_read_idx = 0; fp@1577: tty->tx_write_idx = 0; fp@1577: tty->wakeup = 0; fp@1577: tty->rx_read_idx = 0; fp@1577: tty->rx_write_idx = 0; fp@1577: init_timer(&tty->timer); fp@1577: tty->tty = NULL; fp@1787: tty->ops = *ops; fp@1779: tty->cb_data = cb_data; fp@1577: fp@1577: tty->dev = tty_register_device(tty_driver, tty->minor, NULL); fp@1577: if (IS_ERR(tty->dev)) { fp@1577: printk(KERN_ERR PFX "Failed to register tty device.\n"); fp@1577: return PTR_ERR(tty->dev); fp@1577: } fp@1577: fp@1577: tty->timer.function = ec_tty_wakeup; fp@1577: tty->timer.data = (unsigned long) tty; fp@1577: tty->timer.expires = jiffies + 10; fp@1577: add_timer(&tty->timer); fp@1577: return 0; fp@1577: } fp@1577: fp@1577: /*****************************************************************************/ fp@1577: fp@1577: void ec_tty_clear(ec_tty_t *tty) fp@1577: { fp@1577: del_timer_sync(&tty->timer); fp@1577: tty_unregister_device(tty_driver, tty->minor); fp@1577: } fp@1577: fp@1778: /*****************************************************************************/ fp@1778: fp@1786: unsigned int ec_tty_tx_size(ec_tty_t *tty) fp@1786: { fp@1786: unsigned int ret; fp@1786: fp@1786: if (tty->tx_write_idx >= tty->tx_read_idx) { fp@1786: ret = tty->tx_write_idx - tty->tx_read_idx; fp@1786: } else { fp@1786: ret = EC_TTY_TX_BUFFER_SIZE + tty->tx_write_idx - tty->tx_read_idx; fp@1786: } fp@1786: fp@1786: return ret; fp@1786: } fp@1786: fp@1786: /*****************************************************************************/ fp@1786: fp@1786: unsigned int ec_tty_tx_space(ec_tty_t *tty) fp@1786: { fp@1786: return EC_TTY_TX_BUFFER_SIZE - 1 - ec_tty_tx_size(tty); fp@1786: } fp@1786: fp@1786: /*****************************************************************************/ fp@1786: fp@1786: unsigned int ec_tty_rx_size(ec_tty_t *tty) fp@1786: { fp@1786: unsigned int ret; fp@1786: fp@1786: if (tty->rx_write_idx >= tty->rx_read_idx) { fp@1786: ret = tty->rx_write_idx - tty->rx_read_idx; fp@1786: } else { fp@1786: ret = EC_TTY_RX_BUFFER_SIZE + tty->rx_write_idx - tty->rx_read_idx; fp@1786: } fp@1786: fp@1786: return ret; fp@1786: } fp@1786: fp@1786: /*****************************************************************************/ fp@1786: fp@1786: unsigned int ec_tty_rx_space(ec_tty_t *tty) fp@1786: { fp@1786: return EC_TTY_RX_BUFFER_SIZE - 1 - ec_tty_rx_size(tty); fp@1786: } fp@1786: fp@1786: /*****************************************************************************/ fp@1786: fp@1778: int ec_tty_get_serial_info(ec_tty_t *tty, struct serial_struct *data) fp@1778: { fp@1779: struct serial_struct tmp; fp@1779: fp@1779: if (!data) fp@1778: return -EFAULT; fp@1779: fp@1779: memset(&tmp, 0, sizeof(tmp)); fp@1779: fp@1779: if (copy_to_user(data, &tmp, sizeof(*data))) { fp@1779: return -EFAULT; fp@1779: } fp@1779: return 0; fp@1778: } fp@1778: fp@1786: /*****************************************************************************/ fp@1786: fp@1786: /** Timer function. fp@1786: */ fp@1786: void ec_tty_wakeup(unsigned long data) fp@1786: { fp@1786: ec_tty_t *tty = (ec_tty_t *) data; fp@1786: size_t to_recv; fp@1786: fp@1786: /* Wake up any process waiting to send data */ fp@1786: if (tty->wakeup) { fp@1786: if (tty->tty) { fp@1786: #if EC_TTY_DEBUG >= 1 fp@1786: printk(KERN_INFO PFX "Waking up.\n"); fp@1786: #endif fp@1786: tty_wakeup(tty->tty); fp@1786: } fp@1786: tty->wakeup = 0; fp@1786: } fp@1786: fp@1786: /* Push received data into TTY core. */ fp@1786: to_recv = ec_tty_rx_size(tty); fp@1786: if (to_recv && tty->tty) { fp@1786: unsigned char *cbuf; fp@1786: int space = tty_prepare_flip_string(tty->tty, &cbuf, to_recv); fp@1786: fp@1786: if (space < to_recv) { fp@1786: printk(KERN_WARNING PFX "Insufficient space to_recv=%d space=%d\n", fp@1786: to_recv, space); fp@1786: } fp@1786: fp@1786: if (space < 0) { fp@1786: to_recv = 0; fp@1786: } else { fp@1786: to_recv = space; fp@1786: } fp@1786: fp@1786: if (to_recv) { fp@1786: unsigned int i; fp@1786: fp@1786: #if EC_TTY_DEBUG >= 1 fp@1786: printk(KERN_INFO PFX "Pushing %u bytes to TTY core.\n", to_recv); fp@1786: #endif fp@1786: fp@1786: for (i = 0; i < to_recv; i++) { fp@1786: cbuf[i] = tty->rx_buffer[tty->rx_read_idx]; fp@1786: tty->rx_read_idx = fp@1786: (tty->rx_read_idx + 1) % EC_TTY_RX_BUFFER_SIZE; fp@1786: } fp@1786: tty_flip_buffer_push(tty->tty); fp@1786: } fp@1786: } fp@1786: fp@1786: tty->timer.expires += 1; fp@1786: add_timer(&tty->timer); fp@1786: } fp@1786: fp@1569: /****************************************************************************** fp@1569: * Device callbacks fp@1569: *****************************************************************************/ fp@1569: fp@1568: static int ec_tty_open(struct tty_struct *tty, struct file *file) fp@1568: { fp@1571: ec_tty_t *t; fp@1787: int line = tty->index; fp@1571: fp@1577: #if EC_TTY_DEBUG >= 1 fp@1571: printk(KERN_INFO PFX "Opening line %i.\n", line); fp@1577: #endif fp@1571: fp@1779: if (line < 0 || line >= EC_TTY_MAX_DEVICES) { fp@1779: return -ENXIO; fp@1571: } fp@1571: fp@1571: t = ttys[line]; fp@1571: if (!t) { fp@1571: return -ENXIO; fp@1571: } fp@1571: fp@1575: if (t->tty) { fp@1575: return -EBUSY; fp@1575: } fp@1575: fp@1575: t->tty = tty; fp@1571: tty->driver_data = t; fp@1571: return 0; fp@1568: } fp@1568: fp@1568: /*****************************************************************************/ fp@1568: fp@1568: static void ec_tty_close(struct tty_struct *tty, struct file *file) fp@1568: { fp@1575: ec_tty_t *t = (ec_tty_t *) tty->driver_data; fp@1575: fp@1577: #if EC_TTY_DEBUG >= 1 fp@1571: printk(KERN_INFO PFX "Closing line %i.\n", tty->index); fp@1577: #endif fp@1575: fp@1575: if (t->tty == tty) { fp@1575: t->tty = NULL; fp@1575: } fp@1568: } fp@1568: fp@1568: /*****************************************************************************/ fp@1568: fp@1568: static int ec_tty_write( fp@1568: struct tty_struct *tty, fp@1568: const unsigned char *buffer, fp@1568: int count fp@1568: ) fp@1568: { fp@1575: ec_tty_t *t = (ec_tty_t *) tty->driver_data; fp@1575: unsigned int data_size, i; fp@1575: fp@1577: #if EC_TTY_DEBUG >= 1 fp@1575: printk(KERN_INFO PFX "%s(count=%i)\n", __func__, count); fp@1577: #endif fp@1575: fp@1575: if (count <= 0) { fp@1575: return 0; fp@1575: } fp@1575: fp@1575: data_size = min(ec_tty_tx_space(t), (unsigned int) count); fp@1575: for (i = 0; i < data_size; i++) { fp@1575: t->tx_buffer[t->tx_write_idx] = buffer[i]; fp@1575: t->tx_write_idx = (t->tx_write_idx + 1) % EC_TTY_TX_BUFFER_SIZE; fp@1575: } fp@1575: fp@1577: #if EC_TTY_DEBUG >= 1 fp@1575: printk(KERN_INFO PFX "%s(): %u bytes written.\n", __func__, data_size); fp@1577: #endif fp@1575: return data_size; fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1595: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) fp@1595: static int ec_tty_put_char(struct tty_struct *tty, unsigned char ch) fp@1595: #else fp@1575: static void ec_tty_put_char(struct tty_struct *tty, unsigned char ch) fp@1595: #endif fp@1575: { fp@1575: ec_tty_t *t = (ec_tty_t *) tty->driver_data; fp@1575: fp@1577: #if EC_TTY_DEBUG >= 1 fp@1575: printk(KERN_INFO PFX "%s(): c=%02x.\n", __func__, (unsigned int) ch); fp@1577: #endif fp@1575: fp@1575: if (ec_tty_tx_space(t)) { fp@1575: t->tx_buffer[t->tx_write_idx] = ch; fp@1575: t->tx_write_idx = (t->tx_write_idx + 1) % EC_TTY_TX_BUFFER_SIZE; fp@1595: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) fp@1595: return 1; fp@1595: #endif fp@1575: } else { fp@1575: printk(KERN_WARNING PFX "%s(): Dropped a byte!\n", __func__); fp@1595: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) fp@1595: return 0; fp@1595: #endif fp@1575: } fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1575: static int ec_tty_write_room(struct tty_struct *tty) fp@1575: { fp@1575: ec_tty_t *t = (ec_tty_t *) tty->driver_data; fp@1575: int ret = ec_tty_tx_space(t); fp@1575: fp@1577: #if EC_TTY_DEBUG >= 2 fp@1575: printk(KERN_INFO PFX "%s() = %i.\n", __func__, ret); fp@1577: #endif fp@1575: fp@1575: return ret; fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1575: static int ec_tty_chars_in_buffer(struct tty_struct *tty) fp@1575: { fp@1575: ec_tty_t *t = (ec_tty_t *) tty->driver_data; fp@1575: int ret; fp@1575: fp@1577: #if EC_TTY_DEBUG >= 2 fp@1577: printk(KERN_INFO PFX "%s().\n", __func__); fp@1577: #endif fp@1575: fp@1575: ret = ec_tty_tx_size(t); fp@1575: fp@1577: #if EC_TTY_DEBUG >= 2 fp@1575: printk(KERN_INFO PFX "%s() = %i.\n", __func__, ret); fp@1577: #endif fp@1575: fp@1575: return ret; fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1575: static void ec_tty_flush_buffer(struct tty_struct *tty) fp@1575: { fp@1577: #if EC_TTY_DEBUG >= 2 fp@1577: printk(KERN_INFO PFX "%s().\n", __func__); fp@1577: #endif fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1575: static int ec_tty_ioctl(struct tty_struct *tty, struct file *file, fp@1779: unsigned int cmd, unsigned long arg) fp@1575: { fp@1778: ec_tty_t *t = (ec_tty_t *) tty->driver_data; fp@1778: int ret = -ENOTTY; fp@1778: fp@1778: #if EC_TTY_DEBUG >= 2 fp@1778: printk(KERN_INFO PFX "%s(tty=%p, file=%p, cmd=%08x, arg=%08lx).\n", fp@1778: __func__, tty, file, cmd, arg); fp@1778: printk(KERN_INFO PFX "decoded: type=%02x nr=%u\n", fp@1778: _IOC_TYPE(cmd), _IOC_NR(cmd)); fp@1778: #endif fp@1778: fp@1778: switch (cmd) { fp@1779: case TIOCGSERIAL: fp@1779: if (access_ok(VERIFY_WRITE, fp@1778: (void *) arg, sizeof(struct serial_struct))) { fp@1778: ret = ec_tty_get_serial_info(t, (struct serial_struct *) arg); fp@1778: } else { fp@1778: ret = -EFAULT; fp@1778: } fp@1778: break; fp@1778: fp@1778: case TIOCSSERIAL: // TODO fp@1778: break; fp@1778: fp@1778: default: fp@1778: #if EC_TTY_DEBUG >= 2 fp@1778: printk(KERN_INFO PFX "no ioctl()!\n"); fp@1778: #endif fp@1778: ret = -ENOIOCTLCMD; fp@1778: break; fp@1778: } fp@1778: fp@1778: return ret; fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1575: static void ec_tty_set_termios(struct tty_struct *tty, fp@1779: struct ktermios *old_termios) fp@1779: { fp@1779: ec_tty_t *t = (ec_tty_t *) tty->driver_data; fp@1779: int ret; fp@1778: fp@1577: #if EC_TTY_DEBUG >= 2 fp@1577: printk(KERN_INFO PFX "%s().\n", __func__); fp@1577: #endif fp@1778: fp@1778: if (tty->termios->c_cflag == old_termios->c_cflag) fp@1778: return; fp@1778: fp@1778: #if EC_TTY_DEBUG >= 2 fp@1778: printk(KERN_INFO "cflag changed from %x to %x.\n", fp@1778: old_termios->c_cflag, tty->termios->c_cflag); fp@1778: #endif fp@1779: fp@1787: ret = t->ops.cflag_changed(t->cb_data, tty->termios->c_cflag); fp@1779: if (ret) { fp@1779: printk(KERN_ERR PFX "ERROR: cflag 0x%x not accepted.\n", fp@1779: tty->termios->c_cflag); fp@1779: tty->termios->c_cflag = old_termios->c_cflag; fp@1779: } fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1575: static void ec_tty_stop(struct tty_struct *tty) fp@1575: { fp@1577: #if EC_TTY_DEBUG >= 2 fp@1577: printk(KERN_INFO PFX "%s().\n", __func__); fp@1577: #endif fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1575: static void ec_tty_start(struct tty_struct *tty) fp@1575: { fp@1577: #if EC_TTY_DEBUG >= 2 fp@1577: printk(KERN_INFO PFX "%s().\n", __func__); fp@1577: #endif fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1575: static void ec_tty_hangup(struct tty_struct *tty) fp@1575: { fp@1577: #if EC_TTY_DEBUG >= 2 fp@1577: printk(KERN_INFO PFX "%s().\n", __func__); fp@1577: #endif fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1595: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) fp@1595: static int ec_tty_break(struct tty_struct *tty, int break_state) fp@1595: #else fp@1575: static void ec_tty_break(struct tty_struct *tty, int break_state) fp@1595: #endif fp@1575: { fp@1577: #if EC_TTY_DEBUG >= 2 fp@1575: printk(KERN_INFO PFX "%s(break_state = %i).\n", __func__, break_state); fp@1577: #endif fp@1595: fp@1595: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) fp@1595: return -EIO; // not implemented fp@1595: #endif fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1575: static void ec_tty_send_xchar(struct tty_struct *tty, char ch) fp@1575: { fp@1577: #if EC_TTY_DEBUG >= 2 fp@1575: printk(KERN_INFO PFX "%s(ch=%02x).\n", __func__, (unsigned int) ch); fp@1577: #endif fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1575: static void ec_tty_wait_until_sent(struct tty_struct *tty, int timeout) fp@1575: { fp@1577: #if EC_TTY_DEBUG >= 2 fp@1575: printk(KERN_INFO PFX "%s(timeout=%i).\n", __func__, timeout); fp@1577: #endif fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1575: static const struct tty_operations ec_tty_ops = { fp@1575: .open = ec_tty_open, fp@1575: .close = ec_tty_close, fp@1575: .write = ec_tty_write, fp@1779: .put_char = ec_tty_put_char, fp@1779: .write_room = ec_tty_write_room, fp@1779: .chars_in_buffer = ec_tty_chars_in_buffer, fp@1779: .flush_buffer = ec_tty_flush_buffer, fp@1779: .ioctl = ec_tty_ioctl, fp@1779: .set_termios = ec_tty_set_termios, fp@1779: .stop = ec_tty_stop, fp@1779: .start = ec_tty_start, fp@1779: .hangup = ec_tty_hangup, fp@1779: .break_ctl = ec_tty_break, fp@1779: .send_xchar = ec_tty_send_xchar, fp@1779: .wait_until_sent = ec_tty_wait_until_sent, fp@1575: }; fp@1568: fp@1569: /****************************************************************************** fp@1569: * Public functions and methods fp@1569: *****************************************************************************/ fp@1569: fp@1787: ec_tty_t *ectty_create(const ec_tty_operations_t *ops, void *cb_data) fp@1569: { fp@1569: ec_tty_t *tty; fp@1569: int minor, ret; fp@1569: fp@1569: if (down_interruptible(&tty_sem)) { fp@1569: return ERR_PTR(-EINTR); fp@1569: } fp@1569: fp@1569: for (minor = 0; minor < EC_TTY_MAX_DEVICES; minor++) { fp@1569: if (!ttys[minor]) { fp@1575: printk(KERN_INFO PFX "Creating TTY interface %i.\n", minor); fp@1575: fp@1569: tty = kmalloc(sizeof(ec_tty_t), GFP_KERNEL); fp@1569: if (!tty) { fp@1569: up(&tty_sem); fp@1570: printk(KERN_ERR PFX "Failed to allocate memory.\n"); fp@1569: return ERR_PTR(-ENOMEM); fp@1569: } fp@1569: fp@1787: ret = ec_tty_init(tty, minor, ops, cb_data); fp@1569: if (ret) { fp@1569: up(&tty_sem); fp@1569: kfree(tty); fp@1569: return ERR_PTR(ret); fp@1569: } fp@1569: fp@1569: ttys[minor] = tty; fp@1569: up(&tty_sem); fp@1569: return tty; fp@1569: } fp@1569: } fp@1569: fp@1569: up(&tty_sem); fp@1570: printk(KERN_ERR PFX "No free interfaces avaliable.\n"); fp@1569: return ERR_PTR(-EBUSY); fp@1569: } fp@1569: fp@1569: /*****************************************************************************/ fp@1569: fp@1569: void ectty_free(ec_tty_t *tty) fp@1569: { fp@1575: printk(KERN_INFO PFX "Freeing TTY interface %i.\n", tty->minor); fp@1575: fp@1569: ec_tty_clear(tty); fp@1570: ttys[tty->minor] = NULL; fp@1569: kfree(tty); fp@1569: } fp@1569: fp@1568: /*****************************************************************************/ fp@1568: fp@1575: unsigned int ectty_tx_data(ec_tty_t *tty, uint8_t *buffer, size_t size) fp@1575: { fp@1575: unsigned int data_size = min(ec_tty_tx_size(tty), size), i; fp@1575: fp@1575: if (data_size) { fp@1577: #if EC_TTY_DEBUG >= 1 fp@1575: printk(KERN_INFO PFX "Fetching %u bytes to send.\n", data_size); fp@1577: #endif fp@1575: } fp@1575: fp@1575: for (i = 0; i < data_size; i++) { fp@1575: buffer[i] = tty->tx_buffer[tty->tx_read_idx]; fp@1575: tty->tx_read_idx = (tty->tx_read_idx + 1) % EC_TTY_TX_BUFFER_SIZE; fp@1575: } fp@1575: fp@1575: if (data_size) { fp@1575: tty->wakeup = 1; fp@1575: } fp@1575: fp@1575: return data_size; fp@1575: } fp@1575: fp@1575: /*****************************************************************************/ fp@1575: fp@1577: void ectty_rx_data(ec_tty_t *tty, const uint8_t *buffer, size_t size) fp@1577: { fp@1577: size_t to_recv; fp@1577: fp@1577: if (size) { fp@1577: unsigned int i; fp@1577: fp@1577: #if EC_TTY_DEBUG >= 1 fp@1577: printk(KERN_INFO PFX "Received %u bytes.\n", size); fp@1577: #endif fp@1577: fp@1577: to_recv = min(ec_tty_rx_space(tty), size); fp@1577: fp@1577: if (to_recv < size) { fp@1577: printk(KERN_WARNING PFX "Dropping %u bytes.\n", size - to_recv); fp@1577: } fp@1577: fp@1577: for (i = 0; i < size; i++) { fp@1577: tty->rx_buffer[tty->rx_write_idx] = buffer[i]; fp@1786: tty->rx_write_idx = fp@1786: (tty->rx_write_idx + 1) % EC_TTY_RX_BUFFER_SIZE; fp@1577: } fp@1577: } fp@1577: } fp@1577: fp@1577: /*****************************************************************************/ fp@1577: fp@1565: /** \cond */ fp@1565: fp@1565: module_init(ec_tty_init_module); fp@1565: module_exit(ec_tty_cleanup_module); fp@1565: fp@1569: EXPORT_SYMBOL(ectty_create); fp@1569: EXPORT_SYMBOL(ectty_free); fp@1575: EXPORT_SYMBOL(ectty_tx_data); fp@1577: EXPORT_SYMBOL(ectty_rx_data); fp@1569: fp@1565: /** \endcond */ fp@1565: fp@1565: /*****************************************************************************/