blob: 795bd8102205447527f72e6d01c8d6467c8712c8 [file] [log] [blame]
Jeff Dike165dc592006-01-06 00:18:57 -08001/*
Jeff Dikee99525f2007-10-16 01:26:41 -07002 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * Licensed under the GPL
4 */
5
Linus Torvalds1da177e2005-04-16 15:20:36 -07006#include <linux/slab.h>
7#include <linux/tty.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/tty_flip.h>
Al Viro510c72a32011-08-18 20:08:29 +01009#include "chan.h"
Al Viro37185b32012-10-08 03:27:32 +010010#include <os.h>
11#include <irq_kern.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012
Paolo 'Blaisorblade' Giarrussofac97ae2005-09-22 21:44:22 -070013#ifdef CONFIG_NOCONFIG_CHAN
Jeff Dikef28169d2007-02-10 01:43:53 -080014static void *not_configged_init(char *str, int device,
15 const struct chan_opts *opts)
Paolo 'Blaisorblade' Giarrussofac97ae2005-09-22 21:44:22 -070016{
Jeff Dikee99525f2007-10-16 01:26:41 -070017 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070018 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080019 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070020}
21
22static int not_configged_open(int input, int output, int primary, void *data,
23 char **dev_out)
24{
Jeff Dikee99525f2007-10-16 01:26:41 -070025 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070026 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080027 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -070028}
29
30static void not_configged_close(int fd, void *data)
31{
Jeff Dikee99525f2007-10-16 01:26:41 -070032 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 "UML\n");
34}
35
36static int not_configged_read(int fd, char *c_out, void *data)
37{
Jeff Dikee99525f2007-10-16 01:26:41 -070038 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080040 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070041}
42
43static int not_configged_write(int fd, const char *buf, int len, void *data)
44{
Jeff Dikee99525f2007-10-16 01:26:41 -070045 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080047 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070048}
49
Paolo 'Blaisorblade' Giarrusso55c033c2005-11-13 16:07:11 -080050static int not_configged_console_write(int fd, const char *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070051{
Jeff Dikee99525f2007-10-16 01:26:41 -070052 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080054 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070055}
56
57static int not_configged_window_size(int fd, void *data, unsigned short *rows,
58 unsigned short *cols)
59{
Jeff Dikee99525f2007-10-16 01:26:41 -070060 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 "UML\n");
Jeff Diked50084a2006-01-06 00:18:50 -080062 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -070063}
64
65static void not_configged_free(void *data)
66{
Jeff Dikee99525f2007-10-16 01:26:41 -070067 printk(KERN_ERR "Using a channel type which is configured out of "
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 "UML\n");
69}
70
Jeff Dike5e7672e2006-09-27 01:50:33 -070071static const struct chan_ops not_configged_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 .init = not_configged_init,
73 .open = not_configged_open,
74 .close = not_configged_close,
75 .read = not_configged_read,
76 .write = not_configged_write,
77 .console_write = not_configged_console_write,
78 .window_size = not_configged_window_size,
79 .free = not_configged_free,
80 .winch = 0,
81};
82#endif /* CONFIG_NOCONFIG_CHAN */
83
Jeff Diked50084a2006-01-06 00:18:50 -080084static int open_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085{
Jeff Dike6676ae62007-07-31 00:37:44 -070086 int fd, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
Jeff Dikee99525f2007-10-16 01:26:41 -070088 if (chan->opened)
Jeff Diked50084a2006-01-06 00:18:50 -080089 return 0;
90
Jeff Dikee99525f2007-10-16 01:26:41 -070091 if (chan->ops->open == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -080092 fd = 0;
93 else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
94 chan->data, &chan->dev);
Jeff Dikee99525f2007-10-16 01:26:41 -070095 if (fd < 0)
Jeff Diked50084a2006-01-06 00:18:50 -080096 return fd;
Jeff Dike6676ae62007-07-31 00:37:44 -070097
98 err = os_set_fd_block(fd, 0);
99 if (err) {
100 (*chan->ops->close)(fd, chan->data);
101 return err;
102 }
103
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 chan->fd = fd;
105
106 chan->opened = 1;
Jeff Diked50084a2006-01-06 00:18:50 -0800107 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108}
109
WANG Cong3af7cb72008-04-28 02:13:55 -0700110static int open_chan(struct list_head *chans)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
112 struct list_head *ele;
113 struct chan *chan;
114 int ret, err = 0;
115
Jeff Dikee99525f2007-10-16 01:26:41 -0700116 list_for_each(ele, chans) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 chan = list_entry(ele, struct chan, list);
Jeff Diked50084a2006-01-06 00:18:50 -0800118 ret = open_one_chan(chan);
Jeff Dikee99525f2007-10-16 01:26:41 -0700119 if (chan->primary)
Jeff Diked50084a2006-01-06 00:18:50 -0800120 err = ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 }
Jeff Diked50084a2006-01-06 00:18:50 -0800122 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123}
124
Al Virobed5e392011-09-08 10:49:34 -0400125void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126{
Al Virobed5e392011-09-08 10:49:34 -0400127 if (chan && chan->primary && chan->ops->winch)
128 register_winch(chan->fd, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129}
130
Al Viro0fcd7192011-09-10 08:17:04 -0400131static void line_timer_cb(struct work_struct *work)
132{
133 struct line *line = container_of(work, struct line, task.work);
Jiri Slaby6fc58842012-06-04 13:35:27 +0200134 struct tty_struct *tty = tty_port_tty_get(&line->port);
Al Viro0fcd7192011-09-10 08:17:04 -0400135
136 if (!line->throttled)
Jiri Slaby6fc58842012-06-04 13:35:27 +0200137 chan_interrupt(line, tty, line->driver->read_irq);
138 tty_kref_put(tty);
Al Viro0fcd7192011-09-10 08:17:04 -0400139}
140
Jeff Diked14ad812007-07-15 23:38:54 -0700141int enable_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
143 struct list_head *ele;
144 struct chan *chan;
Jeff Diked14ad812007-07-15 23:38:54 -0700145 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
Al Viro0fcd7192011-09-10 08:17:04 -0400147 INIT_DELAYED_WORK(&line->task, line_timer_cb);
148
Jeff Dikee99525f2007-10-16 01:26:41 -0700149 list_for_each(ele, &line->chan_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 chan = list_entry(ele, struct chan, list);
Jeff Diked14ad812007-07-15 23:38:54 -0700151 err = open_one_chan(chan);
152 if (err) {
153 if (chan->primary)
154 goto out_close;
155
Jeff Dike165dc592006-01-06 00:18:57 -0800156 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700157 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
Jeff Dikee99525f2007-10-16 01:26:41 -0700159 if (chan->enabled)
Jeff Dike165dc592006-01-06 00:18:57 -0800160 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700161 err = line_setup_irq(chan->fd, chan->input, chan->output, line,
162 chan);
163 if (err)
164 goto out_close;
165
Jeff Dike165dc592006-01-06 00:18:57 -0800166 chan->enabled = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 }
Jeff Diked14ad812007-07-15 23:38:54 -0700168
169 return 0;
170
171 out_close:
Al Viro10c890c02011-09-10 08:39:18 -0400172 close_chan(line);
Jeff Diked14ad812007-07-15 23:38:54 -0700173 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174}
175
Jeff Dike190c3e42007-02-10 01:43:55 -0800176/* Items are added in IRQ context, when free_irq can't be called, and
177 * removed in process context, when it can.
178 * This handles interrupt sources which disappear, and which need to
179 * be permanently disabled. This is discovered in IRQ context, but
180 * the freeing of the IRQ must be done later.
181 */
182static DEFINE_SPINLOCK(irqs_to_free_lock);
Jeff Dike165dc592006-01-06 00:18:57 -0800183static LIST_HEAD(irqs_to_free);
184
185void free_irqs(void)
186{
187 struct chan *chan;
Jeff Dike190c3e42007-02-10 01:43:55 -0800188 LIST_HEAD(list);
189 struct list_head *ele;
Jeff Dike30762122007-03-29 01:20:30 -0700190 unsigned long flags;
Jeff Dike165dc592006-01-06 00:18:57 -0800191
Jeff Dike30762122007-03-29 01:20:30 -0700192 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800193 list_splice_init(&irqs_to_free, &list);
Jeff Dike30762122007-03-29 01:20:30 -0700194 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800195
Jeff Dikee99525f2007-10-16 01:26:41 -0700196 list_for_each(ele, &list) {
Jeff Dike190c3e42007-02-10 01:43:55 -0800197 chan = list_entry(ele, struct chan, free_list);
Jeff Dike165dc592006-01-06 00:18:57 -0800198
Richard Weinberger47562272010-08-09 17:20:14 -0700199 if (chan->input && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200200 um_free_irq(chan->line->driver->read_irq, chan);
Richard Weinberger47562272010-08-09 17:20:14 -0700201 if (chan->output && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200202 um_free_irq(chan->line->driver->write_irq, chan);
Jeff Dike165dc592006-01-06 00:18:57 -0800203 chan->enabled = 0;
204 }
205}
206
207static void close_one_chan(struct chan *chan, int delay_free_irq)
208{
Jeff Dike30762122007-03-29 01:20:30 -0700209 unsigned long flags;
210
Jeff Dikee99525f2007-10-16 01:26:41 -0700211 if (!chan->opened)
Jeff Dike165dc592006-01-06 00:18:57 -0800212 return;
213
Jeff Dikee99525f2007-10-16 01:26:41 -0700214 if (delay_free_irq) {
Jeff Dike30762122007-03-29 01:20:30 -0700215 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800216 list_add(&chan->free_list, &irqs_to_free);
Jeff Dike30762122007-03-29 01:20:30 -0700217 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800218 }
219 else {
Richard Weinberger47562272010-08-09 17:20:14 -0700220 if (chan->input && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200221 um_free_irq(chan->line->driver->read_irq, chan);
Richard Weinberger47562272010-08-09 17:20:14 -0700222 if (chan->output && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200223 um_free_irq(chan->line->driver->write_irq, chan);
Jeff Dike165dc592006-01-06 00:18:57 -0800224 chan->enabled = 0;
225 }
Jeff Dikee99525f2007-10-16 01:26:41 -0700226 if (chan->ops->close != NULL)
Jeff Dike165dc592006-01-06 00:18:57 -0800227 (*chan->ops->close)(chan->fd, chan->data);
228
229 chan->opened = 0;
230 chan->fd = -1;
231}
232
Al Viro10c890c02011-09-10 08:39:18 -0400233void close_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234{
235 struct chan *chan;
236
237 /* Close in reverse order as open in case more than one of them
238 * refers to the same device and they save and restore that device's
239 * state. Then, the first one opened will have the original state,
240 * so it must be the last closed.
241 */
Al Viro10c890c02011-09-10 08:39:18 -0400242 list_for_each_entry_reverse(chan, &line->chan_list, list) {
243 close_one_chan(chan, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 }
245}
246
Al Virobed5e392011-09-08 10:49:34 -0400247void deactivate_chan(struct chan *chan, int irq)
Jeff Dikee4dcee82006-01-06 00:18:58 -0800248{
Al Virobed5e392011-09-08 10:49:34 -0400249 if (chan && chan->enabled)
250 deactivate_fd(chan->fd, irq);
Jeff Dikee4dcee82006-01-06 00:18:58 -0800251}
252
Al Virobed5e392011-09-08 10:49:34 -0400253void reactivate_chan(struct chan *chan, int irq)
Jeff Dikee4dcee82006-01-06 00:18:58 -0800254{
Al Virobed5e392011-09-08 10:49:34 -0400255 if (chan && chan->enabled)
256 reactivate_fd(chan->fd, irq);
Jeff Dikee4dcee82006-01-06 00:18:58 -0800257}
258
Al Virobed5e392011-09-08 10:49:34 -0400259int write_chan(struct chan *chan, const char *buf, int len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 int write_irq)
261{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 int n, ret = 0;
263
Al Virobed5e392011-09-08 10:49:34 -0400264 if (len == 0 || !chan || !chan->ops->write)
Jeff Dikec59dbca2007-10-16 01:26:42 -0700265 return 0;
266
Al Virobed5e392011-09-08 10:49:34 -0400267 n = chan->ops->write(chan->fd, buf, len, chan->data);
268 if (chan->primary) {
269 ret = n;
270 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
271 reactivate_fd(chan->fd, write_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 }
Jeff Diked50084a2006-01-06 00:18:50 -0800273 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274}
275
Al Virobed5e392011-09-08 10:49:34 -0400276int console_write_chan(struct chan *chan, const char *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 int n, ret = 0;
279
Al Virobed5e392011-09-08 10:49:34 -0400280 if (!chan || !chan->ops->console_write)
281 return 0;
Jeff Dikee99525f2007-10-16 01:26:41 -0700282
Al Virobed5e392011-09-08 10:49:34 -0400283 n = chan->ops->console_write(chan->fd, buf, len);
284 if (chan->primary)
285 ret = n;
Jeff Diked50084a2006-01-06 00:18:50 -0800286 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287}
288
Jeff Dikea52f3622007-02-10 01:44:06 -0800289int console_open_chan(struct line *line, struct console *co)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
Jeff Dike1f801712006-01-06 00:18:55 -0800291 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
Jeff Dike1f801712006-01-06 00:18:55 -0800293 err = open_chan(&line->chan_list);
Jeff Dikee99525f2007-10-16 01:26:41 -0700294 if (err)
Jeff Dike1f801712006-01-06 00:18:55 -0800295 return err;
296
Jeff Dikee99525f2007-10-16 01:26:41 -0700297 printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
298 co->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 return 0;
300}
301
Al Virobed5e392011-09-08 10:49:34 -0400302int chan_window_size(struct line *line, unsigned short *rows_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 unsigned short *cols_out)
304{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 struct chan *chan;
306
Al Virobed5e392011-09-08 10:49:34 -0400307 chan = line->chan_in;
308 if (chan && chan->primary) {
309 if (chan->ops->window_size == NULL)
310 return 0;
311 return chan->ops->window_size(chan->fd, chan->data,
312 rows_out, cols_out);
313 }
314 chan = line->chan_out;
315 if (chan && chan->primary) {
316 if (chan->ops->window_size == NULL)
317 return 0;
318 return chan->ops->window_size(chan->fd, chan->data,
319 rows_out, cols_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 }
Jeff Diked50084a2006-01-06 00:18:50 -0800321 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322}
323
Al Viro772bd0a2011-08-18 20:12:39 +0100324static void free_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325{
326 list_del(&chan->list);
Jeff Dike165dc592006-01-06 00:18:57 -0800327
Al Viro772bd0a2011-08-18 20:12:39 +0100328 close_one_chan(chan, 0);
Jeff Dike165dc592006-01-06 00:18:57 -0800329
Jeff Dikee99525f2007-10-16 01:26:41 -0700330 if (chan->ops->free != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 (*chan->ops->free)(chan->data);
Jeff Dike165dc592006-01-06 00:18:57 -0800332
Jeff Dikee99525f2007-10-16 01:26:41 -0700333 if (chan->primary && chan->output)
334 ignore_sigio_fd(chan->fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 kfree(chan);
336}
337
Al Viro772bd0a2011-08-18 20:12:39 +0100338static void free_chan(struct list_head *chans)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339{
340 struct list_head *ele, *next;
341 struct chan *chan;
342
Jeff Dikee99525f2007-10-16 01:26:41 -0700343 list_for_each_safe(ele, next, chans) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 chan = list_entry(ele, struct chan, list);
Al Viro772bd0a2011-08-18 20:12:39 +0100345 free_one_chan(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 }
347}
348
349static int one_chan_config_string(struct chan *chan, char *str, int size,
350 char **error_out)
351{
352 int n = 0;
353
Jeff Dikee99525f2007-10-16 01:26:41 -0700354 if (chan == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 CONFIG_CHUNK(str, size, n, "none", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800356 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 }
358
359 CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
360
Jeff Dikee99525f2007-10-16 01:26:41 -0700361 if (chan->dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800363 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 }
365
366 CONFIG_CHUNK(str, size, n, ":", 0);
367 CONFIG_CHUNK(str, size, n, chan->dev, 0);
368
Jeff Diked50084a2006-01-06 00:18:50 -0800369 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370}
371
Jeff Diked50084a2006-01-06 00:18:50 -0800372static int chan_pair_config_string(struct chan *in, struct chan *out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 char *str, int size, char **error_out)
374{
375 int n;
376
377 n = one_chan_config_string(in, str, size, error_out);
378 str += n;
379 size -= n;
380
Jeff Dikee99525f2007-10-16 01:26:41 -0700381 if (in == out) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800383 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 }
385
386 CONFIG_CHUNK(str, size, n, ",", 1);
387 n = one_chan_config_string(out, str, size, error_out);
388 str += n;
389 size -= n;
390 CONFIG_CHUNK(str, size, n, "", 1);
391
Jeff Diked50084a2006-01-06 00:18:50 -0800392 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393}
394
Al Virobed5e392011-09-08 10:49:34 -0400395int chan_config_string(struct line *line, char *str, int size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 char **error_out)
397{
Al Virobed5e392011-09-08 10:49:34 -0400398 struct chan *in = line->chan_in, *out = line->chan_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399
Al Virobed5e392011-09-08 10:49:34 -0400400 if (in && !in->primary)
401 in = NULL;
402 if (out && !out->primary)
403 out = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
Jeff Diked50084a2006-01-06 00:18:50 -0800405 return chan_pair_config_string(in, out, str, size, error_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406}
407
408struct chan_type {
409 char *key;
Jeff Dike5e7672e2006-09-27 01:50:33 -0700410 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411};
412
Jeff Dike5e7672e2006-09-27 01:50:33 -0700413static const struct chan_type chan_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 { "fd", &fd_ops },
415
416#ifdef CONFIG_NULL_CHAN
417 { "null", &null_ops },
418#else
419 { "null", &not_configged_ops },
420#endif
421
422#ifdef CONFIG_PORT_CHAN
423 { "port", &port_ops },
424#else
425 { "port", &not_configged_ops },
426#endif
427
428#ifdef CONFIG_PTY_CHAN
429 { "pty", &pty_ops },
430 { "pts", &pts_ops },
431#else
432 { "pty", &not_configged_ops },
433 { "pts", &not_configged_ops },
434#endif
435
436#ifdef CONFIG_TTY_CHAN
437 { "tty", &tty_ops },
438#else
439 { "tty", &not_configged_ops },
440#endif
441
442#ifdef CONFIG_XTERM_CHAN
443 { "xterm", &xterm_ops },
444#else
445 { "xterm", &not_configged_ops },
446#endif
447};
448
Jeff Dike165dc592006-01-06 00:18:57 -0800449static struct chan *parse_chan(struct line *line, char *str, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800450 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451{
Jeff Dike5e7672e2006-09-27 01:50:33 -0700452 const struct chan_type *entry;
453 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 struct chan *chan;
455 void *data;
456 int i;
457
458 ops = NULL;
459 data = NULL;
Jeff Dikee99525f2007-10-16 01:26:41 -0700460 for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 entry = &chan_table[i];
Jeff Dikee99525f2007-10-16 01:26:41 -0700462 if (!strncmp(str, entry->key, strlen(entry->key))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 ops = entry->ops;
464 str += strlen(entry->key);
465 break;
466 }
467 }
Jeff Dikee99525f2007-10-16 01:26:41 -0700468 if (ops == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800469 *error_out = "No match for configured backends";
Jeff Diked50084a2006-01-06 00:18:50 -0800470 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800472
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 data = (*ops->init)(str, device, opts);
Jeff Dikee99525f2007-10-16 01:26:41 -0700474 if (data == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800475 *error_out = "Configuration failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800476 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800477 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478
Paolo 'Blaisorblade' Giarrusso79ae2cb2005-09-22 21:44:21 -0700479 chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
Jeff Dikee99525f2007-10-16 01:26:41 -0700480 if (chan == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800481 *error_out = "Memory allocation failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800482 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800483 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
Jeff Dike165dc592006-01-06 00:18:57 -0800485 .free_list =
486 LIST_HEAD_INIT(chan->free_list),
487 .line = line,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 .primary = 1,
489 .input = 0,
490 .output = 0,
491 .opened = 0,
Jeff Dike165dc592006-01-06 00:18:57 -0800492 .enabled = 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 .fd = -1,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 .ops = ops,
495 .data = data });
Jeff Diked50084a2006-01-06 00:18:50 -0800496 return chan;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497}
498
Jeff Dike165dc592006-01-06 00:18:57 -0800499int parse_chan_pair(char *str, struct line *line, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800500 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501{
Jeff Dike165dc592006-01-06 00:18:57 -0800502 struct list_head *chans = &line->chan_list;
Richard Weinbergerf1c93e42011-07-25 17:12:55 -0700503 struct chan *new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 char *in, *out;
505
Jeff Dikee99525f2007-10-16 01:26:41 -0700506 if (!list_empty(chans)) {
Al Viroee485072011-09-08 07:07:26 -0400507 line->chan_in = line->chan_out = NULL;
Al Viro772bd0a2011-08-18 20:12:39 +0100508 free_chan(chans);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 INIT_LIST_HEAD(chans);
510 }
511
Al Viro31efceb2011-09-09 19:14:02 -0400512 if (!str)
513 return 0;
514
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 out = strchr(str, ',');
Jeff Dikee99525f2007-10-16 01:26:41 -0700516 if (out != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 in = str;
518 *out = '\0';
519 out++;
Jeff Dikef28169d2007-02-10 01:43:53 -0800520 new = parse_chan(line, in, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700521 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800522 return -1;
523
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 new->input = 1;
525 list_add(&new->list, chans);
Al Viroee485072011-09-08 07:07:26 -0400526 line->chan_in = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
Jeff Dikef28169d2007-02-10 01:43:53 -0800528 new = parse_chan(line, out, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700529 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800530 return -1;
531
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 list_add(&new->list, chans);
533 new->output = 1;
Al Viroee485072011-09-08 07:07:26 -0400534 line->chan_out = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 }
536 else {
Jeff Dikef28169d2007-02-10 01:43:53 -0800537 new = parse_chan(line, str, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700538 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800539 return -1;
540
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 list_add(&new->list, chans);
542 new->input = 1;
543 new->output = 1;
Al Viroee485072011-09-08 07:07:26 -0400544 line->chan_in = line->chan_out = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 }
Jeff Diked50084a2006-01-06 00:18:50 -0800546 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547}
548
Al Viro0fcd7192011-09-10 08:17:04 -0400549void chan_interrupt(struct line *line, struct tty_struct *tty, int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550{
Jiri Slaby227434f2013-01-03 15:53:01 +0100551 struct tty_port *port = &line->port;
Al Virobed5e392011-09-08 10:49:34 -0400552 struct chan *chan = line->chan_in;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 int err;
554 char c;
555
Al Virobed5e392011-09-08 10:49:34 -0400556 if (!chan || !chan->ops->read)
557 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
Al Virobed5e392011-09-08 10:49:34 -0400559 do {
Jiri Slaby227434f2013-01-03 15:53:01 +0100560 if (!tty_buffer_request_room(port, 1)) {
Al Viro0fcd7192011-09-10 08:17:04 -0400561 schedule_delayed_work(&line->task, 1);
Al Virobed5e392011-09-08 10:49:34 -0400562 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 }
Al Virobed5e392011-09-08 10:49:34 -0400564 err = chan->ops->read(chan->fd, &c, chan->data);
565 if (err > 0)
Jiri Slaby92a19f92013-01-03 15:53:03 +0100566 tty_insert_flip_char(port, c, TTY_NORMAL);
Al Virobed5e392011-09-08 10:49:34 -0400567 } while (err > 0);
568
569 if (err == 0)
570 reactivate_fd(chan->fd, irq);
571 if (err == -EIO) {
572 if (chan->primary) {
573 if (tty != NULL)
574 tty_hangup(tty);
Al Viro10c890c02011-09-10 08:39:18 -0400575 if (line->chan_out != chan)
576 close_one_chan(line->chan_out, 1);
Al Virobed5e392011-09-08 10:49:34 -0400577 }
Al Viro10c890c02011-09-10 08:39:18 -0400578 close_one_chan(chan, 1);
579 if (chan->primary)
580 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 }
582 out:
Jeff Dikee99525f2007-10-16 01:26:41 -0700583 if (tty)
584 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585}