Merge branch 'android-tegra-2.6.36' into android-tegra-moto-2.6.36
[firefly-linux-kernel-4.4.55.git] / drivers / misc / ts27010mux / ts27010_tty.c
1 #include <linux/module.h>
2 #include <linux/types.h>
3 #include <linux/slab.h>
4 #include <linux/tty.h>
5 #include <linux/tty_flip.h>
6 #include <linux/kernel.h>
7 #include <linux/string.h>
8
9 #include "ts27010_mux.h"
10 #include "ts27010_ringbuf.h"
11
12 struct ts27010_tty_channel_data {
13         atomic_t ref_count;
14         struct tty_struct *tty;
15 };
16
17 struct ts27010_tty_data {
18         struct ts27010_tty_channel_data         chan[NR_MUXS];
19 };
20
21 /* TODO: find a good place to put this */
22 struct tty_driver *driver;
23
24 /* TODO: should request a major */
25 #define TS0710MUX_MAJOR 245
26 #define TS0710MUX_MINOR_START 0
27
28 int ts27010_tty_send(int line, u8 *data, int len)
29 {
30         struct ts27010_tty_data *td = driver->driver_state;
31         struct tty_struct *tty = td->chan[line].tty;
32
33         if (!tty) {
34                 pr_info("ts27010: mux%d no open.  discarding %d bytes\n",
35                         line, len);
36                 return 0;
37         }
38
39         BUG_ON(tty_insert_flip_string(tty, data, len) != len);
40         tty_flip_buffer_push(tty);
41         return len;
42 }
43
44 int ts27010_tty_send_rbuf(int line, struct ts27010_ringbuf *rbuf,
45                           int data_idx, int len)
46 {
47         struct ts27010_tty_data *td = driver->driver_state;
48         struct tty_struct *tty = td->chan[line].tty;
49
50         if (!tty) {
51                 pr_info("ts27010: mux%d no open.  discarding %d bytes\n",
52                         line, len);
53                 return 0;
54         }
55
56         while (len--) {
57                 char c = ts27010_ringbuf_peek(rbuf, data_idx++);
58                 tty_insert_flip_char(tty, c, TTY_NORMAL);
59         }
60         tty_flip_buffer_push(tty);
61         return len;
62 }
63
64 static int ts27010_tty_open(struct tty_struct *tty, struct file *filp)
65 {
66         struct ts27010_tty_data *td = tty->driver->driver_state;
67         int err;
68         int line;
69
70         if (!ts27010_mux_active()) {
71                 pr_err("ts27010: tty open when line discipline not active.\n");
72                 err = -ENODEV;
73                 goto err;
74         }
75
76         line = tty->index;
77         if ((line < 0) || (line >= NR_MUXS)) {
78                 pr_err("ts27010: tty index out of range.\n");
79                 err = -ENODEV;
80                 goto err;
81         }
82
83         atomic_inc(&td->chan[line].ref_count);
84
85         td->chan[line].tty = tty;
86
87         err = ts27010_mux_line_open(line);
88         if (err < 0)
89                 goto err;
90
91         return 0;
92
93 err:
94         return err;
95 }
96
97
98 static void ts27010_tty_close(struct tty_struct *tty, struct file *filp)
99 {
100         struct ts27010_tty_data *td = tty->driver->driver_state;
101
102         if (atomic_dec_and_test(&td->chan[tty->index].ref_count)) {
103                 ts27010_mux_line_close(tty->index);
104
105                 td->chan[tty->index].tty = NULL;
106
107                 /*
108                  * the old code did:
109                  *   wake_up_interruptible(&tty->read_wait);
110                  *   wake_up_interruptible(&tty->write_wait);
111                  *
112                  * I belive this is unecessary
113                  */
114         }
115 }
116
117 static int ts27010_tty_write(struct tty_struct *tty,
118                              const unsigned char *buf, int count)
119 {
120         return ts27010_mux_line_write(tty->index, buf, count);
121 }
122
123
124 static int ts27010_tty_write_room(struct tty_struct *tty)
125 {
126         return ts27010_mux_line_write_room(tty->index);
127 }
128
129 static void ts27010_tty_flush_buffer(struct tty_struct *tty)
130 {
131         pr_warning("ts27010: flush_buffer not implemented on line %d\n",
132                 tty->index);
133 }
134
135 static int ts27010_tty_chars_in_buffer(struct tty_struct *tty)
136 {
137         return ts27010_mux_line_chars_in_buffer(tty->index);
138 }
139
140 static void ts27010_tty_throttle(struct tty_struct *tty)
141 {
142         pr_warning("ts27010: throttle not implemented on line %d\n",
143                 tty->index);
144 }
145
146 static void ts27010_tty_unthrottle(struct tty_struct *tty)
147 {
148         pr_warning("ts27010: unthrottle not implemented on line %d\n",
149                 tty->index);
150 }
151
152 static int ts27010_tty_ioctl(struct tty_struct *tty, struct file *file,
153                      unsigned int cmd, unsigned long arg)
154 {
155         int line;
156
157         line = tty->index;
158         if ((line < 0) || (line >= NR_MUXS))
159                 return -ENODEV;
160
161         switch (cmd) {
162         case TS0710MUX_IO_MSC_HANGUP:
163                 pr_warning("ts27010: ioctl msc_hangup not implemented\n");
164                 return 0;
165
166         case TS0710MUX_IO_TEST_CMD:
167                 pr_warning("ts27010: ioctl msc_hangup not implemented\n");
168                 return 0;
169
170         default:
171                 break;
172         }
173
174         return -ENOIOCTLCMD;
175 }
176
177 static const struct tty_operations ts27010_tty_ops = {
178         .open = ts27010_tty_open,
179         .close = ts27010_tty_close,
180         .write = ts27010_tty_write,
181         .write_room = ts27010_tty_write_room,
182         .flush_buffer = ts27010_tty_flush_buffer,
183         .chars_in_buffer = ts27010_tty_chars_in_buffer,
184         .throttle = ts27010_tty_throttle,
185         .unthrottle = ts27010_tty_unthrottle,
186         .ioctl = ts27010_tty_ioctl,
187 };
188
189 int ts27010_tty_init(void)
190 {
191         struct ts27010_tty_data *td;
192         int err;
193         int i;
194
195         driver = alloc_tty_driver(NR_MUXS);
196         if (driver == NULL) {
197                 err = -ENOMEM;
198                 goto err0;
199         }
200
201         td = kzalloc(sizeof(*td), GFP_KERNEL);
202         if (td == NULL) {
203                 err = -ENOMEM;
204                 goto err1;
205         }
206
207         for (i = 0; i < NR_MUXS; i++)
208                 atomic_set(&td->chan[i].ref_count, 0);
209
210         driver->driver_state = td;
211
212         driver->driver_name = "ts0710mux";
213         driver->name = "ts0710mux";
214         driver->major = TS0710MUX_MAJOR;
215         driver->major = 234;
216         driver->minor_start = TS0710MUX_MINOR_START;
217         driver->type = TTY_DRIVER_TYPE_SERIAL;
218         driver->subtype = SERIAL_TYPE_NORMAL;
219         driver->init_termios = tty_std_termios;
220         driver->init_termios.c_iflag = 0;
221         driver->init_termios.c_oflag = 0;
222         driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
223         driver->init_termios.c_lflag = 0;
224         driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
225
226         driver->other = NULL;
227         driver->owner = THIS_MODULE;
228
229         tty_set_operations(driver, &ts27010_tty_ops);
230
231         if (tty_register_driver(driver)) {
232                 pr_err("ts27010: can't register tty driver\n");
233                 err = -EINVAL;
234                 goto err2;
235         }
236
237         return 0;
238
239 err2:
240         kfree(td);
241 err1:
242         put_tty_driver(driver);
243 err0:
244         return err;
245 }
246
247 void ts27010_tty_remove(void)
248 {
249         struct ts27010_tty_data *td = driver->driver_state;
250         tty_unregister_driver(driver);
251         kfree(td);
252         put_tty_driver(driver);
253 }
254