aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorNiranjana Vishwanathapura <nvishwan@codeaurora.org>2011-02-09 14:16:34 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2011-02-17 14:25:38 -0500
commitdaaf6ff42d12c89f179868387c0107db6625f0f3 (patch)
tree0eb488d029f82e27e1fbb66e46214d0520467ce4 /drivers
parent0d0389e5414c8950b1613e8bdc74289cde3d6d98 (diff)
tty: Add msm_smd_tty driver
msm_smd_tty driver provides tty device interface to 'DS' and 'GPSNMEA' streaming SMD ports. Cc: Brian Swetland <swetland@google.com> Signed-off-by: Niranjana Vishwanathapura <nvishwan@codeaurora.org> Acked-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/tty/serial/Kconfig9
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--drivers/tty/serial/msm_smd_tty.c236
3 files changed, 246 insertions, 0 deletions
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index aaedbad93a75..90d939a4ee5d 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1600,4 +1600,13 @@ config SERIAL_PCH_UART
1600 Output Hub) which is for IVI(In-Vehicle Infotainment) use. 1600 Output Hub) which is for IVI(In-Vehicle Infotainment) use.
1601 ML7213 is companion chip for Intel Atom E6xx series. 1601 ML7213 is companion chip for Intel Atom E6xx series.
1602 ML7213 is completely compatible for Intel EG20T PCH. 1602 ML7213 is completely compatible for Intel EG20T PCH.
1603
1604config SERIAL_MSM_SMD
1605 bool "Enable tty device interface for some SMD ports"
1606 default n
1607 depends on MSM_SMD
1608 help
1609 Enables userspace clients to read and write to some streaming SMD
1610 ports via tty device interface for MSM chipset.
1611
1603endmenu 1612endmenu
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 8ea92e9c73b0..0c6aefb55acf 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -92,3 +92,4 @@ obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o
92obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o 92obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o
93obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o 93obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o
94obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o 94obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o
95obj-$(CONFIG_SERIAL_MSM_SMD) += msm_smd_tty.o
diff --git a/drivers/tty/serial/msm_smd_tty.c b/drivers/tty/serial/msm_smd_tty.c
new file mode 100644
index 000000000000..beeff1e86093
--- /dev/null
+++ b/drivers/tty/serial/msm_smd_tty.c
@@ -0,0 +1,236 @@
1/* drivers/tty/serial/msm_smd_tty.c
2 *
3 * Copyright (C) 2007 Google, Inc.
4 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
5 * Author: Brian Swetland <swetland@google.com>
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/module.h>
19#include <linux/fs.h>
20#include <linux/cdev.h>
21#include <linux/device.h>
22#include <linux/wait.h>
23
24#include <linux/tty.h>
25#include <linux/tty_driver.h>
26#include <linux/tty_flip.h>
27
28#include <mach/msm_smd.h>
29
30#define MAX_SMD_TTYS 32
31
32struct smd_tty_info {
33 struct tty_port port;
34 smd_channel_t *ch;
35};
36
37struct smd_tty_channel_desc {
38 int id;
39 const char *name;
40};
41
42static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
43
44static const struct smd_tty_channel_desc smd_default_tty_channels[] = {
45 { .id = 0, .name = "SMD_DS" },
46 { .id = 27, .name = "SMD_GPSNMEA" },
47};
48
49static const struct smd_tty_channel_desc *smd_tty_channels =
50 smd_default_tty_channels;
51static int smd_tty_channels_len = ARRAY_SIZE(smd_default_tty_channels);
52
53static void smd_tty_notify(void *priv, unsigned event)
54{
55 unsigned char *ptr;
56 int avail;
57 struct smd_tty_info *info = priv;
58 struct tty_struct *tty;
59
60 if (event != SMD_EVENT_DATA)
61 return;
62
63 tty = tty_port_tty_get(&info->port);
64 if (!tty)
65 return;
66
67 for (;;) {
68 if (test_bit(TTY_THROTTLED, &tty->flags))
69 break;
70 avail = smd_read_avail(info->ch);
71 if (avail == 0)
72 break;
73
74 avail = tty_prepare_flip_string(tty, &ptr, avail);
75
76 if (smd_read(info->ch, ptr, avail) != avail) {
77 /* shouldn't be possible since we're in interrupt
78 ** context here and nobody else could 'steal' our
79 ** characters.
80 */
81 pr_err("OOPS - smd_tty_buffer mismatch?!");
82 }
83
84 tty_flip_buffer_push(tty);
85 }
86
87 /* XXX only when writable and necessary */
88 tty_wakeup(tty);
89 tty_kref_put(tty);
90}
91
92static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty)
93{
94 int i, res = 0;
95 int n = tty->index;
96 const char *name = NULL;
97 struct smd_tty_info *info = smd_tty + n;
98
99 for (i = 0; i < smd_tty_channels_len; i++) {
100 if (smd_tty_channels[i].id == n) {
101 name = smd_tty_channels[i].name;
102 break;
103 }
104 }
105 if (!name)
106 return -ENODEV;
107
108 if (info->ch)
109 smd_kick(info->ch);
110 else
111 res = smd_open(name, &info->ch, info, smd_tty_notify);
112
113 if (!res)
114 tty->driver_data = info;
115
116 return res;
117}
118
119static void smd_tty_port_shutdown(struct tty_port *tport)
120{
121 struct smd_tty_info *info;
122 struct tty_struct *tty = tty_port_tty_get(tport);
123
124 info = tty->driver_data;
125 if (info->ch) {
126 smd_close(info->ch);
127 info->ch = 0;
128 }
129
130 tty->driver_data = 0;
131 tty_kref_put(tty);
132}
133
134static int smd_tty_open(struct tty_struct *tty, struct file *f)
135{
136 struct smd_tty_info *info = smd_tty + tty->index;
137
138 return tty_port_open(&info->port, tty, f);
139}
140
141static void smd_tty_close(struct tty_struct *tty, struct file *f)
142{
143 struct smd_tty_info *info = tty->driver_data;
144
145 tty_port_close(&info->port, tty, f);
146}
147
148static int smd_tty_write(struct tty_struct *tty,
149 const unsigned char *buf, int len)
150{
151 struct smd_tty_info *info = tty->driver_data;
152 int avail;
153
154 /* if we're writing to a packet channel we will
155 ** never be able to write more data than there
156 ** is currently space for
157 */
158 avail = smd_write_avail(info->ch);
159 if (len > avail)
160 len = avail;
161
162 return smd_write(info->ch, buf, len);
163}
164
165static int smd_tty_write_room(struct tty_struct *tty)
166{
167 struct smd_tty_info *info = tty->driver_data;
168 return smd_write_avail(info->ch);
169}
170
171static int smd_tty_chars_in_buffer(struct tty_struct *tty)
172{
173 struct smd_tty_info *info = tty->driver_data;
174 return smd_read_avail(info->ch);
175}
176
177static void smd_tty_unthrottle(struct tty_struct *tty)
178{
179 struct smd_tty_info *info = tty->driver_data;
180 smd_kick(info->ch);
181}
182
183static const struct tty_port_operations smd_tty_port_ops = {
184 .shutdown = smd_tty_port_shutdown,
185 .activate = smd_tty_port_activate,
186};
187
188static const struct tty_operations smd_tty_ops = {
189 .open = smd_tty_open,
190 .close = smd_tty_close,
191 .write = smd_tty_write,
192 .write_room = smd_tty_write_room,
193 .chars_in_buffer = smd_tty_chars_in_buffer,
194 .unthrottle = smd_tty_unthrottle,
195};
196
197static struct tty_driver *smd_tty_driver;
198
199static int __init smd_tty_init(void)
200{
201 int ret, i;
202
203 smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
204 if (smd_tty_driver == 0)
205 return -ENOMEM;
206
207 smd_tty_driver->owner = THIS_MODULE;
208 smd_tty_driver->driver_name = "smd_tty_driver";
209 smd_tty_driver->name = "smd";
210 smd_tty_driver->major = 0;
211 smd_tty_driver->minor_start = 0;
212 smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
213 smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
214 smd_tty_driver->init_termios = tty_std_termios;
215 smd_tty_driver->init_termios.c_iflag = 0;
216 smd_tty_driver->init_termios.c_oflag = 0;
217 smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
218 smd_tty_driver->init_termios.c_lflag = 0;
219 smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
220 TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
221 tty_set_operations(smd_tty_driver, &smd_tty_ops);
222
223 ret = tty_register_driver(smd_tty_driver);
224 if (ret)
225 return ret;
226
227 for (i = 0; i < smd_tty_channels_len; i++) {
228 tty_port_init(&smd_tty[smd_tty_channels[i].id].port);
229 smd_tty[smd_tty_channels[i].id].port.ops = &smd_tty_port_ops;
230 tty_register_device(smd_tty_driver, smd_tty_channels[i].id, 0);
231 }
232
233 return 0;
234}
235
236module_init(smd_tty_init);