diff options
Diffstat (limited to 'arch/um/drivers/chan_kern.c')
-rw-r--r-- | arch/um/drivers/chan_kern.c | 198 |
1 files changed, 88 insertions, 110 deletions
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c index 420e2c800799..ca4c7ebfd0aa 100644 --- a/arch/um/drivers/chan_kern.c +++ b/arch/um/drivers/chan_kern.c | |||
@@ -140,18 +140,18 @@ static int open_chan(struct list_head *chans) | |||
140 | return err; | 140 | return err; |
141 | } | 141 | } |
142 | 142 | ||
143 | void chan_enable_winch(struct list_head *chans, struct tty_struct *tty) | 143 | void chan_enable_winch(struct chan *chan, struct tty_struct *tty) |
144 | { | 144 | { |
145 | struct list_head *ele; | 145 | if (chan && chan->primary && chan->ops->winch) |
146 | struct chan *chan; | 146 | register_winch(chan->fd, tty); |
147 | } | ||
147 | 148 | ||
148 | list_for_each(ele, chans) { | 149 | static void line_timer_cb(struct work_struct *work) |
149 | chan = list_entry(ele, struct chan, list); | 150 | { |
150 | if (chan->primary && chan->output && chan->ops->winch) { | 151 | struct line *line = container_of(work, struct line, task.work); |
151 | register_winch(chan->fd, tty); | 152 | |
152 | return; | 153 | if (!line->throttled) |
153 | } | 154 | chan_interrupt(line, line->tty, line->driver->read_irq); |
154 | } | ||
155 | } | 155 | } |
156 | 156 | ||
157 | int enable_chan(struct line *line) | 157 | int enable_chan(struct line *line) |
@@ -160,6 +160,8 @@ int enable_chan(struct line *line) | |||
160 | struct chan *chan; | 160 | struct chan *chan; |
161 | int err; | 161 | int err; |
162 | 162 | ||
163 | INIT_DELAYED_WORK(&line->task, line_timer_cb); | ||
164 | |||
163 | list_for_each(ele, &line->chan_list) { | 165 | list_for_each(ele, &line->chan_list) { |
164 | chan = list_entry(ele, struct chan, list); | 166 | chan = list_entry(ele, struct chan, list); |
165 | err = open_one_chan(chan); | 167 | err = open_one_chan(chan); |
@@ -183,7 +185,7 @@ int enable_chan(struct line *line) | |||
183 | return 0; | 185 | return 0; |
184 | 186 | ||
185 | out_close: | 187 | out_close: |
186 | close_chan(&line->chan_list, 0); | 188 | close_chan(line); |
187 | return err; | 189 | return err; |
188 | } | 190 | } |
189 | 191 | ||
@@ -244,7 +246,7 @@ static void close_one_chan(struct chan *chan, int delay_free_irq) | |||
244 | chan->fd = -1; | 246 | chan->fd = -1; |
245 | } | 247 | } |
246 | 248 | ||
247 | void close_chan(struct list_head *chans, int delay_free_irq) | 249 | void close_chan(struct line *line) |
248 | { | 250 | { |
249 | struct chan *chan; | 251 | struct chan *chan; |
250 | 252 | ||
@@ -253,77 +255,50 @@ void close_chan(struct list_head *chans, int delay_free_irq) | |||
253 | * state. Then, the first one opened will have the original state, | 255 | * state. Then, the first one opened will have the original state, |
254 | * so it must be the last closed. | 256 | * so it must be the last closed. |
255 | */ | 257 | */ |
256 | list_for_each_entry_reverse(chan, chans, list) { | 258 | list_for_each_entry_reverse(chan, &line->chan_list, list) { |
257 | close_one_chan(chan, delay_free_irq); | 259 | close_one_chan(chan, 0); |
258 | } | 260 | } |
259 | } | 261 | } |
260 | 262 | ||
261 | void deactivate_chan(struct list_head *chans, int irq) | 263 | void deactivate_chan(struct chan *chan, int irq) |
262 | { | 264 | { |
263 | struct list_head *ele; | 265 | if (chan && chan->enabled) |
264 | 266 | deactivate_fd(chan->fd, irq); | |
265 | struct chan *chan; | ||
266 | list_for_each(ele, chans) { | ||
267 | chan = list_entry(ele, struct chan, list); | ||
268 | |||
269 | if (chan->enabled && chan->input) | ||
270 | deactivate_fd(chan->fd, irq); | ||
271 | } | ||
272 | } | 267 | } |
273 | 268 | ||
274 | void reactivate_chan(struct list_head *chans, int irq) | 269 | void reactivate_chan(struct chan *chan, int irq) |
275 | { | 270 | { |
276 | struct list_head *ele; | 271 | if (chan && chan->enabled) |
277 | struct chan *chan; | 272 | reactivate_fd(chan->fd, irq); |
278 | |||
279 | list_for_each(ele, chans) { | ||
280 | chan = list_entry(ele, struct chan, list); | ||
281 | |||
282 | if (chan->enabled && chan->input) | ||
283 | reactivate_fd(chan->fd, irq); | ||
284 | } | ||
285 | } | 273 | } |
286 | 274 | ||
287 | int write_chan(struct list_head *chans, const char *buf, int len, | 275 | int write_chan(struct chan *chan, const char *buf, int len, |
288 | int write_irq) | 276 | int write_irq) |
289 | { | 277 | { |
290 | struct list_head *ele; | ||
291 | struct chan *chan = NULL; | ||
292 | int n, ret = 0; | 278 | int n, ret = 0; |
293 | 279 | ||
294 | if (len == 0) | 280 | if (len == 0 || !chan || !chan->ops->write) |
295 | return 0; | 281 | return 0; |
296 | 282 | ||
297 | list_for_each(ele, chans) { | 283 | n = chan->ops->write(chan->fd, buf, len, chan->data); |
298 | chan = list_entry(ele, struct chan, list); | 284 | if (chan->primary) { |
299 | if (!chan->output || (chan->ops->write == NULL)) | 285 | ret = n; |
300 | continue; | 286 | if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len))) |
301 | 287 | reactivate_fd(chan->fd, write_irq); | |
302 | n = chan->ops->write(chan->fd, buf, len, chan->data); | ||
303 | if (chan->primary) { | ||
304 | ret = n; | ||
305 | if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len))) | ||
306 | reactivate_fd(chan->fd, write_irq); | ||
307 | } | ||
308 | } | 288 | } |
309 | return ret; | 289 | return ret; |
310 | } | 290 | } |
311 | 291 | ||
312 | int console_write_chan(struct list_head *chans, const char *buf, int len) | 292 | int console_write_chan(struct chan *chan, const char *buf, int len) |
313 | { | 293 | { |
314 | struct list_head *ele; | ||
315 | struct chan *chan; | ||
316 | int n, ret = 0; | 294 | int n, ret = 0; |
317 | 295 | ||
318 | list_for_each(ele, chans) { | 296 | if (!chan || !chan->ops->console_write) |
319 | chan = list_entry(ele, struct chan, list); | 297 | return 0; |
320 | if (!chan->output || (chan->ops->console_write == NULL)) | ||
321 | continue; | ||
322 | 298 | ||
323 | n = chan->ops->console_write(chan->fd, buf, len); | 299 | n = chan->ops->console_write(chan->fd, buf, len); |
324 | if (chan->primary) | 300 | if (chan->primary) |
325 | ret = n; | 301 | ret = n; |
326 | } | ||
327 | return ret; | 302 | return ret; |
328 | } | 303 | } |
329 | 304 | ||
@@ -340,20 +315,24 @@ int console_open_chan(struct line *line, struct console *co) | |||
340 | return 0; | 315 | return 0; |
341 | } | 316 | } |
342 | 317 | ||
343 | int chan_window_size(struct list_head *chans, unsigned short *rows_out, | 318 | int chan_window_size(struct line *line, unsigned short *rows_out, |
344 | unsigned short *cols_out) | 319 | unsigned short *cols_out) |
345 | { | 320 | { |
346 | struct list_head *ele; | ||
347 | struct chan *chan; | 321 | struct chan *chan; |
348 | 322 | ||
349 | list_for_each(ele, chans) { | 323 | chan = line->chan_in; |
350 | chan = list_entry(ele, struct chan, list); | 324 | if (chan && chan->primary) { |
351 | if (chan->primary) { | 325 | if (chan->ops->window_size == NULL) |
352 | if (chan->ops->window_size == NULL) | 326 | return 0; |
353 | return 0; | 327 | return chan->ops->window_size(chan->fd, chan->data, |
354 | return chan->ops->window_size(chan->fd, chan->data, | 328 | rows_out, cols_out); |
355 | rows_out, cols_out); | 329 | } |
356 | } | 330 | chan = line->chan_out; |
331 | if (chan && chan->primary) { | ||
332 | if (chan->ops->window_size == NULL) | ||
333 | return 0; | ||
334 | return chan->ops->window_size(chan->fd, chan->data, | ||
335 | rows_out, cols_out); | ||
357 | } | 336 | } |
358 | return 0; | 337 | return 0; |
359 | } | 338 | } |
@@ -429,21 +408,15 @@ static int chan_pair_config_string(struct chan *in, struct chan *out, | |||
429 | return n; | 408 | return n; |
430 | } | 409 | } |
431 | 410 | ||
432 | int chan_config_string(struct list_head *chans, char *str, int size, | 411 | int chan_config_string(struct line *line, char *str, int size, |
433 | char **error_out) | 412 | char **error_out) |
434 | { | 413 | { |
435 | struct list_head *ele; | 414 | struct chan *in = line->chan_in, *out = line->chan_out; |
436 | struct chan *chan, *in = NULL, *out = NULL; | ||
437 | 415 | ||
438 | list_for_each(ele, chans) { | 416 | if (in && !in->primary) |
439 | chan = list_entry(ele, struct chan, list); | 417 | in = NULL; |
440 | if (!chan->primary) | 418 | if (out && !out->primary) |
441 | continue; | 419 | out = NULL; |
442 | if (chan->input) | ||
443 | in = chan; | ||
444 | if (chan->output) | ||
445 | out = chan; | ||
446 | } | ||
447 | 420 | ||
448 | return chan_pair_config_string(in, out, str, size, error_out); | 421 | return chan_pair_config_string(in, out, str, size, error_out); |
449 | } | 422 | } |
@@ -547,10 +520,14 @@ int parse_chan_pair(char *str, struct line *line, int device, | |||
547 | char *in, *out; | 520 | char *in, *out; |
548 | 521 | ||
549 | if (!list_empty(chans)) { | 522 | if (!list_empty(chans)) { |
523 | line->chan_in = line->chan_out = NULL; | ||
550 | free_chan(chans); | 524 | free_chan(chans); |
551 | INIT_LIST_HEAD(chans); | 525 | INIT_LIST_HEAD(chans); |
552 | } | 526 | } |
553 | 527 | ||
528 | if (!str) | ||
529 | return 0; | ||
530 | |||
554 | out = strchr(str, ','); | 531 | out = strchr(str, ','); |
555 | if (out != NULL) { | 532 | if (out != NULL) { |
556 | in = str; | 533 | in = str; |
@@ -562,6 +539,7 @@ int parse_chan_pair(char *str, struct line *line, int device, | |||
562 | 539 | ||
563 | new->input = 1; | 540 | new->input = 1; |
564 | list_add(&new->list, chans); | 541 | list_add(&new->list, chans); |
542 | line->chan_in = new; | ||
565 | 543 | ||
566 | new = parse_chan(line, out, device, opts, error_out); | 544 | new = parse_chan(line, out, device, opts, error_out); |
567 | if (new == NULL) | 545 | if (new == NULL) |
@@ -569,6 +547,7 @@ int parse_chan_pair(char *str, struct line *line, int device, | |||
569 | 547 | ||
570 | list_add(&new->list, chans); | 548 | list_add(&new->list, chans); |
571 | new->output = 1; | 549 | new->output = 1; |
550 | line->chan_out = new; | ||
572 | } | 551 | } |
573 | else { | 552 | else { |
574 | new = parse_chan(line, str, device, opts, error_out); | 553 | new = parse_chan(line, str, device, opts, error_out); |
@@ -578,43 +557,42 @@ int parse_chan_pair(char *str, struct line *line, int device, | |||
578 | list_add(&new->list, chans); | 557 | list_add(&new->list, chans); |
579 | new->input = 1; | 558 | new->input = 1; |
580 | new->output = 1; | 559 | new->output = 1; |
560 | line->chan_in = line->chan_out = new; | ||
581 | } | 561 | } |
582 | return 0; | 562 | return 0; |
583 | } | 563 | } |
584 | 564 | ||
585 | void chan_interrupt(struct list_head *chans, struct delayed_work *task, | 565 | void chan_interrupt(struct line *line, struct tty_struct *tty, int irq) |
586 | struct tty_struct *tty, int irq) | ||
587 | { | 566 | { |
588 | struct list_head *ele, *next; | 567 | struct chan *chan = line->chan_in; |
589 | struct chan *chan; | ||
590 | int err; | 568 | int err; |
591 | char c; | 569 | char c; |
592 | 570 | ||
593 | list_for_each_safe(ele, next, chans) { | 571 | if (!chan || !chan->ops->read) |
594 | chan = list_entry(ele, struct chan, list); | 572 | goto out; |
595 | if (!chan->input || (chan->ops->read == NULL)) | 573 | |
596 | continue; | 574 | do { |
597 | do { | 575 | if (tty && !tty_buffer_request_room(tty, 1)) { |
598 | if (tty && !tty_buffer_request_room(tty, 1)) { | 576 | schedule_delayed_work(&line->task, 1); |
599 | schedule_delayed_work(task, 1); | 577 | goto out; |
600 | goto out; | ||
601 | } | ||
602 | err = chan->ops->read(chan->fd, &c, chan->data); | ||
603 | if (err > 0) | ||
604 | tty_receive_char(tty, c); | ||
605 | } while (err > 0); | ||
606 | |||
607 | if (err == 0) | ||
608 | reactivate_fd(chan->fd, irq); | ||
609 | if (err == -EIO) { | ||
610 | if (chan->primary) { | ||
611 | if (tty != NULL) | ||
612 | tty_hangup(tty); | ||
613 | close_chan(chans, 1); | ||
614 | return; | ||
615 | } | ||
616 | else close_one_chan(chan, 1); | ||
617 | } | 578 | } |
579 | err = chan->ops->read(chan->fd, &c, chan->data); | ||
580 | if (err > 0) | ||
581 | tty_receive_char(tty, c); | ||
582 | } while (err > 0); | ||
583 | |||
584 | if (err == 0) | ||
585 | reactivate_fd(chan->fd, irq); | ||
586 | if (err == -EIO) { | ||
587 | if (chan->primary) { | ||
588 | if (tty != NULL) | ||
589 | tty_hangup(tty); | ||
590 | if (line->chan_out != chan) | ||
591 | close_one_chan(line->chan_out, 1); | ||
592 | } | ||
593 | close_one_chan(chan, 1); | ||
594 | if (chan->primary) | ||
595 | return; | ||
618 | } | 596 | } |
619 | out: | 597 | out: |
620 | if (tty) | 598 | if (tty) |