diff options
author | Niranjana Vishwanathapura <nvishwan@codeaurora.org> | 2011-02-09 14:16:34 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-02-17 14:25:38 -0500 |
commit | daaf6ff42d12c89f179868387c0107db6625f0f3 (patch) | |
tree | 0eb488d029f82e27e1fbb66e46214d0520467ce4 /drivers | |
parent | 0d0389e5414c8950b1613e8bdc74289cde3d6d98 (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/Kconfig | 9 | ||||
-rw-r--r-- | drivers/tty/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/tty/serial/msm_smd_tty.c | 236 |
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 | |||
1604 | config 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 | |||
1603 | endmenu | 1612 | endmenu |
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 | |||
92 | obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o | 92 | obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o |
93 | obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o | 93 | obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o |
94 | obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o | 94 | obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o |
95 | obj-$(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 | |||
32 | struct smd_tty_info { | ||
33 | struct tty_port port; | ||
34 | smd_channel_t *ch; | ||
35 | }; | ||
36 | |||
37 | struct smd_tty_channel_desc { | ||
38 | int id; | ||
39 | const char *name; | ||
40 | }; | ||
41 | |||
42 | static struct smd_tty_info smd_tty[MAX_SMD_TTYS]; | ||
43 | |||
44 | static const struct smd_tty_channel_desc smd_default_tty_channels[] = { | ||
45 | { .id = 0, .name = "SMD_DS" }, | ||
46 | { .id = 27, .name = "SMD_GPSNMEA" }, | ||
47 | }; | ||
48 | |||
49 | static const struct smd_tty_channel_desc *smd_tty_channels = | ||
50 | smd_default_tty_channels; | ||
51 | static int smd_tty_channels_len = ARRAY_SIZE(smd_default_tty_channels); | ||
52 | |||
53 | static 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 | |||
92 | static 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 | |||
119 | static 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 | |||
134 | static 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 | |||
141 | static 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 | |||
148 | static 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 | |||
165 | static 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 | |||
171 | static 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 | |||
177 | static 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 | |||
183 | static const struct tty_port_operations smd_tty_port_ops = { | ||
184 | .shutdown = smd_tty_port_shutdown, | ||
185 | .activate = smd_tty_port_activate, | ||
186 | }; | ||
187 | |||
188 | static 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 | |||
197 | static struct tty_driver *smd_tty_driver; | ||
198 | |||
199 | static 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 | |||
236 | module_init(smd_tty_init); | ||