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