linux/drivers/tty/serial/msm_smd_tty.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007 Google, Inc.
   3 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
   4 * Author: Brian Swetland <swetland@google.com>
   5 *
   6 * This software is licensed under the terms of the GNU General Public
   7 * License version 2, as published by the Free Software Foundation, and
   8 * may be copied, distributed, and modified under those terms.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 */
  16
  17#include <linux/module.h>
  18#include <linux/fs.h>
  19#include <linux/cdev.h>
  20#include <linux/device.h>
  21#include <linux/wait.h>
  22
  23#include <linux/tty.h>
  24#include <linux/tty_driver.h>
  25#include <linux/tty_flip.h>
  26
  27#include <mach/msm_smd.h>
  28
  29#define MAX_SMD_TTYS 32
  30
  31struct smd_tty_info {
  32        struct tty_port port;
  33        smd_channel_t *ch;
  34};
  35
  36struct smd_tty_channel_desc {
  37        int id;
  38        const char *name;
  39};
  40
  41static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
  42
  43static const struct smd_tty_channel_desc smd_default_tty_channels[] = {
  44        { .id = 0, .name = "SMD_DS" },
  45        { .id = 27, .name = "SMD_GPSNMEA" },
  46};
  47
  48static const struct smd_tty_channel_desc *smd_tty_channels =
  49                smd_default_tty_channels;
  50static int smd_tty_channels_len = ARRAY_SIZE(smd_default_tty_channels);
  51
  52static void smd_tty_notify(void *priv, unsigned event)
  53{
  54        unsigned char *ptr;
  55        int avail;
  56        struct smd_tty_info *info = priv;
  57        struct tty_struct *tty;
  58
  59        if (event != SMD_EVENT_DATA)
  60                return;
  61
  62        tty = tty_port_tty_get(&info->port);
  63        if (!tty)
  64                return;
  65
  66        for (;;) {
  67                if (test_bit(TTY_THROTTLED, &tty->flags))
  68                        break;
  69                avail = smd_read_avail(info->ch);
  70                if (avail == 0)
  71                        break;
  72
  73                avail = tty_prepare_flip_string(&info->port, &ptr, avail);
  74
  75                if (smd_read(info->ch, ptr, avail) != avail) {
  76                        /* shouldn't be possible since we're in interrupt
  77                        ** context here and nobody else could 'steal' our
  78                        ** characters.
  79                        */
  80                        pr_err("OOPS - smd_tty_buffer mismatch?!");
  81                }
  82
  83                tty_flip_buffer_push(&info->port);
  84        }
  85
  86        /* XXX only when writable and necessary */
  87        tty_wakeup(tty);
  88        tty_kref_put(tty);
  89}
  90
  91static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty)
  92{
  93        struct smd_tty_info *info = container_of(tport, struct smd_tty_info,
  94                        port);
  95        int i, res = 0;
  96        const char *name = NULL;
  97
  98        for (i = 0; i < smd_tty_channels_len; i++) {
  99                if (smd_tty_channels[i].id == tty->index) {
 100                        name = smd_tty_channels[i].name;
 101                        break;
 102                }
 103        }
 104        if (!name)
 105                return -ENODEV;
 106
 107        if (info->ch)
 108                smd_kick(info->ch);
 109        else
 110                res = smd_open(name, &info->ch, info, smd_tty_notify);
 111
 112        if (!res)
 113                tty->driver_data = info;
 114
 115        return res;
 116}
 117
 118static void smd_tty_port_shutdown(struct tty_port *tport)
 119{
 120        struct smd_tty_info *info = container_of(tport, struct smd_tty_info,
 121                        port);
 122
 123        if (info->ch) {
 124                smd_close(info->ch);
 125                info->ch = 0;
 126        }
 127}
 128
 129static int smd_tty_open(struct tty_struct *tty, struct file *f)
 130{
 131        struct smd_tty_info *info = smd_tty + tty->index;
 132
 133        return tty_port_open(&info->port, tty, f);
 134}
 135
 136static void smd_tty_close(struct tty_struct *tty, struct file *f)
 137{
 138        struct smd_tty_info *info = tty->driver_data;
 139
 140        tty_port_close(&info->port, tty, f);
 141}
 142
 143static int smd_tty_write(struct tty_struct *tty,
 144                         const unsigned char *buf, int len)
 145{
 146        struct smd_tty_info *info = tty->driver_data;
 147        int avail;
 148
 149        /* if we're writing to a packet channel we will
 150        ** never be able to write more data than there
 151        ** is currently space for
 152        */
 153        avail = smd_write_avail(info->ch);
 154        if (len > avail)
 155                len = avail;
 156
 157        return smd_write(info->ch, buf, len);
 158}
 159
 160static int smd_tty_write_room(struct tty_struct *tty)
 161{
 162        struct smd_tty_info *info = tty->driver_data;
 163        return smd_write_avail(info->ch);
 164}
 165
 166static int smd_tty_chars_in_buffer(struct tty_struct *tty)
 167{
 168        struct smd_tty_info *info = tty->driver_data;
 169        return smd_read_avail(info->ch);
 170}
 171
 172static void smd_tty_unthrottle(struct tty_struct *tty)
 173{
 174        struct smd_tty_info *info = tty->driver_data;
 175        smd_kick(info->ch);
 176}
 177
 178static const struct tty_port_operations smd_tty_port_ops = {
 179        .shutdown = smd_tty_port_shutdown,
 180        .activate = smd_tty_port_activate,
 181};
 182
 183static const struct tty_operations smd_tty_ops = {
 184        .open = smd_tty_open,
 185        .close = smd_tty_close,
 186        .write = smd_tty_write,
 187        .write_room = smd_tty_write_room,
 188        .chars_in_buffer = smd_tty_chars_in_buffer,
 189        .unthrottle = smd_tty_unthrottle,
 190};
 191
 192static struct tty_driver *smd_tty_driver;
 193
 194static int __init smd_tty_init(void)
 195{
 196        int ret, i;
 197
 198        smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
 199        if (smd_tty_driver == 0)
 200                return -ENOMEM;
 201
 202        smd_tty_driver->driver_name = "smd_tty_driver";
 203        smd_tty_driver->name = "smd";
 204        smd_tty_driver->major = 0;
 205        smd_tty_driver->minor_start = 0;
 206        smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
 207        smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
 208        smd_tty_driver->init_termios = tty_std_termios;
 209        smd_tty_driver->init_termios.c_iflag = 0;
 210        smd_tty_driver->init_termios.c_oflag = 0;
 211        smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
 212        smd_tty_driver->init_termios.c_lflag = 0;
 213        smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
 214                TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
 215        tty_set_operations(smd_tty_driver, &smd_tty_ops);
 216
 217        ret = tty_register_driver(smd_tty_driver);
 218        if (ret)
 219                return ret;
 220
 221        for (i = 0; i < smd_tty_channels_len; i++) {
 222                struct tty_port *port = &smd_tty[smd_tty_channels[i].id].port;
 223                tty_port_init(port);
 224                port->ops = &smd_tty_port_ops;
 225                tty_port_register_device(port, smd_tty_driver,
 226                                smd_tty_channels[i].id, NULL);
 227        }
 228
 229        return 0;
 230}
 231
 232module_init(smd_tty_init);
 233