blob: 4ff2503a1bb831a7f83535d3b5907bf90bbaee48 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070084static void tty_receive_char(struct tty_struct *tty, char ch)
85{
Jiri Slabyd2ffc742012-10-25 16:40:59 +020086 if (tty)
87 tty_insert_flip_char(tty, ch, TTY_NORMAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088}
89
Jeff Diked50084a2006-01-06 00:18:50 -080090static int open_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -070091{
Jeff Dike6676ae62007-07-31 00:37:44 -070092 int fd, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
Jeff Dikee99525f2007-10-16 01:26:41 -070094 if (chan->opened)
Jeff Diked50084a2006-01-06 00:18:50 -080095 return 0;
96
Jeff Dikee99525f2007-10-16 01:26:41 -070097 if (chan->ops->open == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -080098 fd = 0;
99 else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
100 chan->data, &chan->dev);
Jeff Dikee99525f2007-10-16 01:26:41 -0700101 if (fd < 0)
Jeff Diked50084a2006-01-06 00:18:50 -0800102 return fd;
Jeff Dike6676ae62007-07-31 00:37:44 -0700103
104 err = os_set_fd_block(fd, 0);
105 if (err) {
106 (*chan->ops->close)(fd, chan->data);
107 return err;
108 }
109
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 chan->fd = fd;
111
112 chan->opened = 1;
Jeff Diked50084a2006-01-06 00:18:50 -0800113 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114}
115
WANG Cong3af7cb72008-04-28 02:13:55 -0700116static int open_chan(struct list_head *chans)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117{
118 struct list_head *ele;
119 struct chan *chan;
120 int ret, err = 0;
121
Jeff Dikee99525f2007-10-16 01:26:41 -0700122 list_for_each(ele, chans) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 chan = list_entry(ele, struct chan, list);
Jeff Diked50084a2006-01-06 00:18:50 -0800124 ret = open_one_chan(chan);
Jeff Dikee99525f2007-10-16 01:26:41 -0700125 if (chan->primary)
Jeff Diked50084a2006-01-06 00:18:50 -0800126 err = ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 }
Jeff Diked50084a2006-01-06 00:18:50 -0800128 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129}
130
Al Virobed5e392011-09-08 10:49:34 -0400131void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132{
Al Virobed5e392011-09-08 10:49:34 -0400133 if (chan && chan->primary && chan->ops->winch)
134 register_winch(chan->fd, tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135}
136
Al Viro0fcd7192011-09-10 08:17:04 -0400137static void line_timer_cb(struct work_struct *work)
138{
139 struct line *line = container_of(work, struct line, task.work);
Jiri Slaby6fc58842012-06-04 13:35:27 +0200140 struct tty_struct *tty = tty_port_tty_get(&line->port);
Al Viro0fcd7192011-09-10 08:17:04 -0400141
142 if (!line->throttled)
Jiri Slaby6fc58842012-06-04 13:35:27 +0200143 chan_interrupt(line, tty, line->driver->read_irq);
144 tty_kref_put(tty);
Al Viro0fcd7192011-09-10 08:17:04 -0400145}
146
Jeff Diked14ad812007-07-15 23:38:54 -0700147int enable_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
149 struct list_head *ele;
150 struct chan *chan;
Jeff Diked14ad812007-07-15 23:38:54 -0700151 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Al Viro0fcd7192011-09-10 08:17:04 -0400153 INIT_DELAYED_WORK(&line->task, line_timer_cb);
154
Jeff Dikee99525f2007-10-16 01:26:41 -0700155 list_for_each(ele, &line->chan_list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 chan = list_entry(ele, struct chan, list);
Jeff Diked14ad812007-07-15 23:38:54 -0700157 err = open_one_chan(chan);
158 if (err) {
159 if (chan->primary)
160 goto out_close;
161
Jeff Dike165dc592006-01-06 00:18:57 -0800162 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700163 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164
Jeff Dikee99525f2007-10-16 01:26:41 -0700165 if (chan->enabled)
Jeff Dike165dc592006-01-06 00:18:57 -0800166 continue;
Jeff Diked14ad812007-07-15 23:38:54 -0700167 err = line_setup_irq(chan->fd, chan->input, chan->output, line,
168 chan);
169 if (err)
170 goto out_close;
171
Jeff Dike165dc592006-01-06 00:18:57 -0800172 chan->enabled = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 }
Jeff Diked14ad812007-07-15 23:38:54 -0700174
175 return 0;
176
177 out_close:
Al Viro10c890c02011-09-10 08:39:18 -0400178 close_chan(line);
Jeff Diked14ad812007-07-15 23:38:54 -0700179 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180}
181
Jeff Dike190c3e42007-02-10 01:43:55 -0800182/* Items are added in IRQ context, when free_irq can't be called, and
183 * removed in process context, when it can.
184 * This handles interrupt sources which disappear, and which need to
185 * be permanently disabled. This is discovered in IRQ context, but
186 * the freeing of the IRQ must be done later.
187 */
188static DEFINE_SPINLOCK(irqs_to_free_lock);
Jeff Dike165dc592006-01-06 00:18:57 -0800189static LIST_HEAD(irqs_to_free);
190
191void free_irqs(void)
192{
193 struct chan *chan;
Jeff Dike190c3e42007-02-10 01:43:55 -0800194 LIST_HEAD(list);
195 struct list_head *ele;
Jeff Dike30762122007-03-29 01:20:30 -0700196 unsigned long flags;
Jeff Dike165dc592006-01-06 00:18:57 -0800197
Jeff Dike30762122007-03-29 01:20:30 -0700198 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800199 list_splice_init(&irqs_to_free, &list);
Jeff Dike30762122007-03-29 01:20:30 -0700200 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike190c3e42007-02-10 01:43:55 -0800201
Jeff Dikee99525f2007-10-16 01:26:41 -0700202 list_for_each(ele, &list) {
Jeff Dike190c3e42007-02-10 01:43:55 -0800203 chan = list_entry(ele, struct chan, free_list);
Jeff Dike165dc592006-01-06 00:18:57 -0800204
Richard Weinberger47562272010-08-09 17:20:14 -0700205 if (chan->input && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200206 um_free_irq(chan->line->driver->read_irq, chan);
Richard Weinberger47562272010-08-09 17:20:14 -0700207 if (chan->output && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200208 um_free_irq(chan->line->driver->write_irq, chan);
Jeff Dike165dc592006-01-06 00:18:57 -0800209 chan->enabled = 0;
210 }
211}
212
213static void close_one_chan(struct chan *chan, int delay_free_irq)
214{
Jeff Dike30762122007-03-29 01:20:30 -0700215 unsigned long flags;
216
Jeff Dikee99525f2007-10-16 01:26:41 -0700217 if (!chan->opened)
Jeff Dike165dc592006-01-06 00:18:57 -0800218 return;
219
Jeff Dikee99525f2007-10-16 01:26:41 -0700220 if (delay_free_irq) {
Jeff Dike30762122007-03-29 01:20:30 -0700221 spin_lock_irqsave(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800222 list_add(&chan->free_list, &irqs_to_free);
Jeff Dike30762122007-03-29 01:20:30 -0700223 spin_unlock_irqrestore(&irqs_to_free_lock, flags);
Jeff Dike165dc592006-01-06 00:18:57 -0800224 }
225 else {
Richard Weinberger47562272010-08-09 17:20:14 -0700226 if (chan->input && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200227 um_free_irq(chan->line->driver->read_irq, chan);
Richard Weinberger47562272010-08-09 17:20:14 -0700228 if (chan->output && chan->enabled)
Richard Weinbergerfa7a0442012-04-17 22:37:13 +0200229 um_free_irq(chan->line->driver->write_irq, chan);
Jeff Dike165dc592006-01-06 00:18:57 -0800230 chan->enabled = 0;
231 }
Jeff Dikee99525f2007-10-16 01:26:41 -0700232 if (chan->ops->close != NULL)
Jeff Dike165dc592006-01-06 00:18:57 -0800233 (*chan->ops->close)(chan->fd, chan->data);
234
235 chan->opened = 0;
236 chan->fd = -1;
237}
238
Al Viro10c890c02011-09-10 08:39:18 -0400239void close_chan(struct line *line)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240{
241 struct chan *chan;
242
243 /* Close in reverse order as open in case more than one of them
244 * refers to the same device and they save and restore that device's
245 * state. Then, the first one opened will have the original state,
246 * so it must be the last closed.
247 */
Al Viro10c890c02011-09-10 08:39:18 -0400248 list_for_each_entry_reverse(chan, &line->chan_list, list) {
249 close_one_chan(chan, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 }
251}
252
Al Virobed5e392011-09-08 10:49:34 -0400253void deactivate_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 deactivate_fd(chan->fd, irq);
Jeff Dikee4dcee82006-01-06 00:18:58 -0800257}
258
Al Virobed5e392011-09-08 10:49:34 -0400259void reactivate_chan(struct chan *chan, int irq)
Jeff Dikee4dcee82006-01-06 00:18:58 -0800260{
Al Virobed5e392011-09-08 10:49:34 -0400261 if (chan && chan->enabled)
262 reactivate_fd(chan->fd, irq);
Jeff Dikee4dcee82006-01-06 00:18:58 -0800263}
264
Al Virobed5e392011-09-08 10:49:34 -0400265int write_chan(struct chan *chan, const char *buf, int len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 int write_irq)
267{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 int n, ret = 0;
269
Al Virobed5e392011-09-08 10:49:34 -0400270 if (len == 0 || !chan || !chan->ops->write)
Jeff Dikec59dbca2007-10-16 01:26:42 -0700271 return 0;
272
Al Virobed5e392011-09-08 10:49:34 -0400273 n = chan->ops->write(chan->fd, buf, len, chan->data);
274 if (chan->primary) {
275 ret = n;
276 if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
277 reactivate_fd(chan->fd, write_irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 }
Jeff Diked50084a2006-01-06 00:18:50 -0800279 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280}
281
Al Virobed5e392011-09-08 10:49:34 -0400282int console_write_chan(struct chan *chan, const char *buf, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 int n, ret = 0;
285
Al Virobed5e392011-09-08 10:49:34 -0400286 if (!chan || !chan->ops->console_write)
287 return 0;
Jeff Dikee99525f2007-10-16 01:26:41 -0700288
Al Virobed5e392011-09-08 10:49:34 -0400289 n = chan->ops->console_write(chan->fd, buf, len);
290 if (chan->primary)
291 ret = n;
Jeff Diked50084a2006-01-06 00:18:50 -0800292 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293}
294
Jeff Dikea52f3622007-02-10 01:44:06 -0800295int console_open_chan(struct line *line, struct console *co)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
Jeff Dike1f801712006-01-06 00:18:55 -0800297 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
Jeff Dike1f801712006-01-06 00:18:55 -0800299 err = open_chan(&line->chan_list);
Jeff Dikee99525f2007-10-16 01:26:41 -0700300 if (err)
Jeff Dike1f801712006-01-06 00:18:55 -0800301 return err;
302
Jeff Dikee99525f2007-10-16 01:26:41 -0700303 printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
304 co->index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 return 0;
306}
307
Al Virobed5e392011-09-08 10:49:34 -0400308int chan_window_size(struct line *line, unsigned short *rows_out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 unsigned short *cols_out)
310{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 struct chan *chan;
312
Al Virobed5e392011-09-08 10:49:34 -0400313 chan = line->chan_in;
314 if (chan && chan->primary) {
315 if (chan->ops->window_size == NULL)
316 return 0;
317 return chan->ops->window_size(chan->fd, chan->data,
318 rows_out, cols_out);
319 }
320 chan = line->chan_out;
321 if (chan && chan->primary) {
322 if (chan->ops->window_size == NULL)
323 return 0;
324 return chan->ops->window_size(chan->fd, chan->data,
325 rows_out, cols_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
Jeff Diked50084a2006-01-06 00:18:50 -0800327 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328}
329
Al Viro772bd0a2011-08-18 20:12:39 +0100330static void free_one_chan(struct chan *chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331{
332 list_del(&chan->list);
Jeff Dike165dc592006-01-06 00:18:57 -0800333
Al Viro772bd0a2011-08-18 20:12:39 +0100334 close_one_chan(chan, 0);
Jeff Dike165dc592006-01-06 00:18:57 -0800335
Jeff Dikee99525f2007-10-16 01:26:41 -0700336 if (chan->ops->free != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 (*chan->ops->free)(chan->data);
Jeff Dike165dc592006-01-06 00:18:57 -0800338
Jeff Dikee99525f2007-10-16 01:26:41 -0700339 if (chan->primary && chan->output)
340 ignore_sigio_fd(chan->fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 kfree(chan);
342}
343
Al Viro772bd0a2011-08-18 20:12:39 +0100344static void free_chan(struct list_head *chans)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345{
346 struct list_head *ele, *next;
347 struct chan *chan;
348
Jeff Dikee99525f2007-10-16 01:26:41 -0700349 list_for_each_safe(ele, next, chans) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 chan = list_entry(ele, struct chan, list);
Al Viro772bd0a2011-08-18 20:12:39 +0100351 free_one_chan(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 }
353}
354
355static int one_chan_config_string(struct chan *chan, char *str, int size,
356 char **error_out)
357{
358 int n = 0;
359
Jeff Dikee99525f2007-10-16 01:26:41 -0700360 if (chan == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 CONFIG_CHUNK(str, size, n, "none", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800362 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 }
364
365 CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
366
Jeff Dikee99525f2007-10-16 01:26:41 -0700367 if (chan->dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800369 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 }
371
372 CONFIG_CHUNK(str, size, n, ":", 0);
373 CONFIG_CHUNK(str, size, n, chan->dev, 0);
374
Jeff Diked50084a2006-01-06 00:18:50 -0800375 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376}
377
Jeff Diked50084a2006-01-06 00:18:50 -0800378static int chan_pair_config_string(struct chan *in, struct chan *out,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 char *str, int size, char **error_out)
380{
381 int n;
382
383 n = one_chan_config_string(in, str, size, error_out);
384 str += n;
385 size -= n;
386
Jeff Dikee99525f2007-10-16 01:26:41 -0700387 if (in == out) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 CONFIG_CHUNK(str, size, n, "", 1);
Jeff Diked50084a2006-01-06 00:18:50 -0800389 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 }
391
392 CONFIG_CHUNK(str, size, n, ",", 1);
393 n = one_chan_config_string(out, str, size, error_out);
394 str += n;
395 size -= n;
396 CONFIG_CHUNK(str, size, n, "", 1);
397
Jeff Diked50084a2006-01-06 00:18:50 -0800398 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399}
400
Al Virobed5e392011-09-08 10:49:34 -0400401int chan_config_string(struct line *line, char *str, int size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 char **error_out)
403{
Al Virobed5e392011-09-08 10:49:34 -0400404 struct chan *in = line->chan_in, *out = line->chan_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
Al Virobed5e392011-09-08 10:49:34 -0400406 if (in && !in->primary)
407 in = NULL;
408 if (out && !out->primary)
409 out = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
Jeff Diked50084a2006-01-06 00:18:50 -0800411 return chan_pair_config_string(in, out, str, size, error_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412}
413
414struct chan_type {
415 char *key;
Jeff Dike5e7672e2006-09-27 01:50:33 -0700416 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417};
418
Jeff Dike5e7672e2006-09-27 01:50:33 -0700419static const struct chan_type chan_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 { "fd", &fd_ops },
421
422#ifdef CONFIG_NULL_CHAN
423 { "null", &null_ops },
424#else
425 { "null", &not_configged_ops },
426#endif
427
428#ifdef CONFIG_PORT_CHAN
429 { "port", &port_ops },
430#else
431 { "port", &not_configged_ops },
432#endif
433
434#ifdef CONFIG_PTY_CHAN
435 { "pty", &pty_ops },
436 { "pts", &pts_ops },
437#else
438 { "pty", &not_configged_ops },
439 { "pts", &not_configged_ops },
440#endif
441
442#ifdef CONFIG_TTY_CHAN
443 { "tty", &tty_ops },
444#else
445 { "tty", &not_configged_ops },
446#endif
447
448#ifdef CONFIG_XTERM_CHAN
449 { "xterm", &xterm_ops },
450#else
451 { "xterm", &not_configged_ops },
452#endif
453};
454
Jeff Dike165dc592006-01-06 00:18:57 -0800455static struct chan *parse_chan(struct line *line, char *str, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800456 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457{
Jeff Dike5e7672e2006-09-27 01:50:33 -0700458 const struct chan_type *entry;
459 const struct chan_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 struct chan *chan;
461 void *data;
462 int i;
463
464 ops = NULL;
465 data = NULL;
Jeff Dikee99525f2007-10-16 01:26:41 -0700466 for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 entry = &chan_table[i];
Jeff Dikee99525f2007-10-16 01:26:41 -0700468 if (!strncmp(str, entry->key, strlen(entry->key))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 ops = entry->ops;
470 str += strlen(entry->key);
471 break;
472 }
473 }
Jeff Dikee99525f2007-10-16 01:26:41 -0700474 if (ops == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800475 *error_out = "No match for configured backends";
Jeff Diked50084a2006-01-06 00:18:50 -0800476 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800478
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 data = (*ops->init)(str, device, opts);
Jeff Dikee99525f2007-10-16 01:26:41 -0700480 if (data == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800481 *error_out = "Configuration 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
Paolo 'Blaisorblade' Giarrusso79ae2cb2005-09-22 21:44:21 -0700485 chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
Jeff Dikee99525f2007-10-16 01:26:41 -0700486 if (chan == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800487 *error_out = "Memory allocation failed";
Jeff Diked50084a2006-01-06 00:18:50 -0800488 return NULL;
Jeff Dikef28169d2007-02-10 01:43:53 -0800489 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
Jeff Dike165dc592006-01-06 00:18:57 -0800491 .free_list =
492 LIST_HEAD_INIT(chan->free_list),
493 .line = line,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 .primary = 1,
495 .input = 0,
496 .output = 0,
497 .opened = 0,
Jeff Dike165dc592006-01-06 00:18:57 -0800498 .enabled = 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 .fd = -1,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 .ops = ops,
501 .data = data });
Jeff Diked50084a2006-01-06 00:18:50 -0800502 return chan;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503}
504
Jeff Dike165dc592006-01-06 00:18:57 -0800505int parse_chan_pair(char *str, struct line *line, int device,
Jeff Dikef28169d2007-02-10 01:43:53 -0800506 const struct chan_opts *opts, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507{
Jeff Dike165dc592006-01-06 00:18:57 -0800508 struct list_head *chans = &line->chan_list;
Richard Weinbergerf1c93e42011-07-25 17:12:55 -0700509 struct chan *new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 char *in, *out;
511
Jeff Dikee99525f2007-10-16 01:26:41 -0700512 if (!list_empty(chans)) {
Al Viroee485072011-09-08 07:07:26 -0400513 line->chan_in = line->chan_out = NULL;
Al Viro772bd0a2011-08-18 20:12:39 +0100514 free_chan(chans);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 INIT_LIST_HEAD(chans);
516 }
517
Al Viro31efceb2011-09-09 19:14:02 -0400518 if (!str)
519 return 0;
520
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 out = strchr(str, ',');
Jeff Dikee99525f2007-10-16 01:26:41 -0700522 if (out != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 in = str;
524 *out = '\0';
525 out++;
Jeff Dikef28169d2007-02-10 01:43:53 -0800526 new = parse_chan(line, in, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700527 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800528 return -1;
529
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 new->input = 1;
531 list_add(&new->list, chans);
Al Viroee485072011-09-08 07:07:26 -0400532 line->chan_in = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Jeff Dikef28169d2007-02-10 01:43:53 -0800534 new = parse_chan(line, out, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700535 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800536 return -1;
537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 list_add(&new->list, chans);
539 new->output = 1;
Al Viroee485072011-09-08 07:07:26 -0400540 line->chan_out = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 }
542 else {
Jeff Dikef28169d2007-02-10 01:43:53 -0800543 new = parse_chan(line, str, device, opts, error_out);
Jeff Dikee99525f2007-10-16 01:26:41 -0700544 if (new == NULL)
Jeff Diked50084a2006-01-06 00:18:50 -0800545 return -1;
546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 list_add(&new->list, chans);
548 new->input = 1;
549 new->output = 1;
Al Viroee485072011-09-08 07:07:26 -0400550 line->chan_in = line->chan_out = new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 }
Jeff Diked50084a2006-01-06 00:18:50 -0800552 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553}
554
Al Viro0fcd7192011-09-10 08:17:04 -0400555void chan_interrupt(struct line *line, struct tty_struct *tty, int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556{
Jiri Slaby227434f2013-01-03 15:53:01 +0100557 struct tty_port *port = &line->port;
Al Virobed5e392011-09-08 10:49:34 -0400558 struct chan *chan = line->chan_in;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 int err;
560 char c;
561
Al Virobed5e392011-09-08 10:49:34 -0400562 if (!chan || !chan->ops->read)
563 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Al Virobed5e392011-09-08 10:49:34 -0400565 do {
Jiri Slaby227434f2013-01-03 15:53:01 +0100566 if (!tty_buffer_request_room(port, 1)) {
Al Viro0fcd7192011-09-10 08:17:04 -0400567 schedule_delayed_work(&line->task, 1);
Al Virobed5e392011-09-08 10:49:34 -0400568 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 }
Al Virobed5e392011-09-08 10:49:34 -0400570 err = chan->ops->read(chan->fd, &c, chan->data);
571 if (err > 0)
572 tty_receive_char(tty, c);
573 } while (err > 0);
574
575 if (err == 0)
576 reactivate_fd(chan->fd, irq);
577 if (err == -EIO) {
578 if (chan->primary) {
579 if (tty != NULL)
580 tty_hangup(tty);
Al Viro10c890c02011-09-10 08:39:18 -0400581 if (line->chan_out != chan)
582 close_one_chan(line->chan_out, 1);
Al Virobed5e392011-09-08 10:49:34 -0400583 }
Al Viro10c890c02011-09-10 08:39:18 -0400584 close_one_chan(chan, 1);
585 if (chan->primary)
586 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 }
588 out:
Jeff Dikee99525f2007-10-16 01:26:41 -0700589 if (tty)
590 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591}