Staging: dream: remove wakelock support
[cascardo/linux.git] / drivers / staging / dream / smd / smd_tty.c
1 /* arch/arm/mach-msm/smd_tty.c
2  *
3  * Copyright (C) 2007 Google, Inc.
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
31 static DEFINE_MUTEX(smd_tty_lock);
32
33 struct smd_tty_info {
34         smd_channel_t *ch;
35         struct tty_struct *tty;
36         int open_count;
37 };
38
39 static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
40
41
42 static void smd_tty_notify(void *priv, unsigned event)
43 {
44         unsigned char *ptr;
45         int avail;
46         struct smd_tty_info *info = priv;
47         struct tty_struct *tty = info->tty;
48
49         if (!tty)
50                 return;
51
52         if (event != SMD_EVENT_DATA)
53                 return;
54
55         for (;;) {
56                 if (test_bit(TTY_THROTTLED, &tty->flags)) break;
57                 avail = smd_read_avail(info->ch);
58                 if (avail == 0) break;
59
60                 avail = tty_prepare_flip_string(tty, &ptr, avail);
61
62                 if (smd_read(info->ch, ptr, avail) != avail) {
63                         /* shouldn't be possible since we're in interrupt
64                         ** context here and nobody else could 'steal' our
65                         ** characters.
66                         */
67                         printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!");
68                 }
69
70                 tty_flip_buffer_push(tty);
71         }
72
73         /* XXX only when writable and necessary */
74         tty_wakeup(tty);
75 }
76
77 static int smd_tty_open(struct tty_struct *tty, struct file *f)
78 {
79         int res = 0;
80         int n = tty->index;
81         struct smd_tty_info *info;
82         const char *name;
83
84         if (n == 0) {
85                 name = "SMD_DS";
86         } else if (n == 27) {
87                 name = "SMD_GPSNMEA";
88         } else {
89                 return -ENODEV;
90         }
91
92         info = smd_tty + n;
93
94         mutex_lock(&smd_tty_lock);
95         tty->driver_data = info;
96
97         if (info->open_count++ == 0) {
98                 info->tty = tty;
99                 if (info->ch) {
100                         smd_kick(info->ch);
101                 } else {
102                         res = smd_open(name, &info->ch, info, smd_tty_notify);
103                 }
104         }
105         mutex_unlock(&smd_tty_lock);
106
107         return res;
108 }
109
110 static void smd_tty_close(struct tty_struct *tty, struct file *f)
111 {
112         struct smd_tty_info *info = tty->driver_data;
113
114         if (info == 0)
115                 return;
116
117         mutex_lock(&smd_tty_lock);
118         if (--info->open_count == 0) {
119                 info->tty = 0;
120                 tty->driver_data = 0;
121                 if (info->ch) {
122                         smd_close(info->ch);
123                         info->ch = 0;
124                 }
125         }
126         mutex_unlock(&smd_tty_lock);
127 }
128
129 static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len)
130 {
131         struct smd_tty_info *info = tty->driver_data;
132         int avail;
133
134         /* if we're writing to a packet channel we will
135         ** never be able to write more data than there
136         ** is currently space for
137         */
138         avail = smd_write_avail(info->ch);
139         if (len > avail)
140                 len = avail;
141
142         return smd_write(info->ch, buf, len);
143 }
144
145 static int smd_tty_write_room(struct tty_struct *tty)
146 {
147         struct smd_tty_info *info = tty->driver_data;
148         return smd_write_avail(info->ch);
149 }
150
151 static int smd_tty_chars_in_buffer(struct tty_struct *tty)
152 {
153         struct smd_tty_info *info = tty->driver_data;
154         return smd_read_avail(info->ch);
155 }
156
157 static void smd_tty_unthrottle(struct tty_struct *tty)
158 {
159         struct smd_tty_info *info = tty->driver_data;
160         smd_kick(info->ch);
161 }
162
163 static struct tty_operations smd_tty_ops = {
164         .open = smd_tty_open,
165         .close = smd_tty_close,
166         .write = smd_tty_write,
167         .write_room = smd_tty_write_room,
168         .chars_in_buffer = smd_tty_chars_in_buffer,
169         .unthrottle = smd_tty_unthrottle,
170 };
171
172 static struct tty_driver *smd_tty_driver;
173
174 static int __init smd_tty_init(void)
175 {
176         int ret;
177
178         smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
179         if (smd_tty_driver == 0)
180                 return -ENOMEM;
181
182         smd_tty_driver->owner = THIS_MODULE;
183         smd_tty_driver->driver_name = "smd_tty_driver";
184         smd_tty_driver->name = "smd";
185         smd_tty_driver->major = 0;
186         smd_tty_driver->minor_start = 0;
187         smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
188         smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
189         smd_tty_driver->init_termios = tty_std_termios;
190         smd_tty_driver->init_termios.c_iflag = 0;
191         smd_tty_driver->init_termios.c_oflag = 0;
192         smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
193         smd_tty_driver->init_termios.c_lflag = 0;
194         smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
195                 TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
196         tty_set_operations(smd_tty_driver, &smd_tty_ops);
197
198         ret = tty_register_driver(smd_tty_driver);
199         if (ret) return ret;
200
201         /* this should be dynamic */
202         tty_register_device(smd_tty_driver, 0, 0);
203         tty_register_device(smd_tty_driver, 27, 0);
204
205         return 0;
206 }
207
208 module_init(smd_tty_init);