aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/drivers
diff options
context:
space:
mode:
authorJeff Dike <jdike@addtoit.com>2006-01-06 03:18:57 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-06 11:33:46 -0500
commit165dc5911627a9c4752e909a0da661b96b6fd269 (patch)
tree7e8eb47b7e766a5296b9e15ef177f958dec16d93 /arch/um/drivers
parent1f80171e81ed0d08dcdb6efe239d7b929aef498f (diff)
[PATCH] uml: Simplify console opening/closing and irq registration
This patch simplifies the opening and closing of host console devices and the registration and deregistration of IRQs. The intent is to make it obvious that an IRQ can't exist without an open file descriptor. chan_enable will now open the channel, and when both opening and IRQ registration are desired, this should be used. Opening only is done for the initial console, so that interface still needs to exist. The free_irqs_later interface is now gone. It was intended to avoid freeing an IRQ while it was being processed. It did this, but it didn't eliminate the possiblity of free_irq being called from an interrupt, which is bad. In its place is a list of irqs to be freed, which is processed by the signal handler just before exiting. close_one_chan now disables irqs. When a host device disappears, it is just closed, and that disables IRQs. The device id registered with the IRQ is now the chan structure, not the tty. This is because the interrupt arrives on a descriptor associated with the channel. This caused equivalent changes in the arguments to line_timer_cb. line_disable is gone since it is not used any more. The count field in the line structure is gone. tty->count is used instead. The complicated logic in sigio_handler with freeing IRQs when necessary and making sure its idea of the next irq is correct is now much simpler. The irq list can't be rearranged underneath it, so it is now a simple list walk. Signed-off-by: Jeff Dike <jdike@addtoit.com> Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/um/drivers')
-rw-r--r--arch/um/drivers/chan_kern.c111
-rw-r--r--arch/um/drivers/line.c116
2 files changed, 120 insertions, 107 deletions
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
index 31b69c4ea800..1bb920c0d77a 100644
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -1,4 +1,4 @@
1/* 1/*
2 * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) 2 * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL 3 * Licensed under the GPL
4 */ 4 */
@@ -240,20 +240,65 @@ void chan_enable_winch(struct list_head *chans, struct tty_struct *tty)
240 } 240 }
241} 241}
242 242
243void enable_chan(struct list_head *chans, struct tty_struct *tty) 243void enable_chan(struct line *line)
244{ 244{
245 struct list_head *ele; 245 struct list_head *ele;
246 struct chan *chan; 246 struct chan *chan;
247 247
248 list_for_each(ele, chans){ 248 list_for_each(ele, &line->chan_list){
249 chan = list_entry(ele, struct chan, list); 249 chan = list_entry(ele, struct chan, list);
250 if(!chan->opened) continue; 250 if(open_one_chan(chan))
251 continue;
252
253 if(chan->enabled)
254 continue;
255 line_setup_irq(chan->fd, chan->input, chan->output, line,
256 chan);
257 chan->enabled = 1;
258 }
259}
260
261static LIST_HEAD(irqs_to_free);
262
263void free_irqs(void)
264{
265 struct chan *chan;
266
267 while(!list_empty(&irqs_to_free)){
268 chan = list_entry(irqs_to_free.next, struct chan, free_list);
269 list_del(&chan->free_list);
270
271 if(chan->input)
272 free_irq(chan->line->driver->read_irq, chan);
273 if(chan->output)
274 free_irq(chan->line->driver->write_irq, chan);
275 chan->enabled = 0;
276 }
277}
278
279static void close_one_chan(struct chan *chan, int delay_free_irq)
280{
281 if(!chan->opened)
282 return;
251 283
252 line_setup_irq(chan->fd, chan->input, chan->output, tty); 284 if(delay_free_irq){
285 list_add(&chan->free_list, &irqs_to_free);
286 }
287 else {
288 if(chan->input)
289 free_irq(chan->line->driver->read_irq, chan);
290 if(chan->output)
291 free_irq(chan->line->driver->write_irq, chan);
292 chan->enabled = 0;
253 } 293 }
294 if(chan->ops->close != NULL)
295 (*chan->ops->close)(chan->fd, chan->data);
296
297 chan->opened = 0;
298 chan->fd = -1;
254} 299}
255 300
256void close_chan(struct list_head *chans) 301void close_chan(struct list_head *chans, int delay_free_irq)
257{ 302{
258 struct chan *chan; 303 struct chan *chan;
259 304
@@ -263,11 +308,7 @@ void close_chan(struct list_head *chans)
263 * so it must be the last closed. 308 * so it must be the last closed.
264 */ 309 */
265 list_for_each_entry_reverse(chan, chans, list) { 310 list_for_each_entry_reverse(chan, chans, list) {
266 if(!chan->opened) continue; 311 close_one_chan(chan, delay_free_irq);
267 if(chan->ops->close != NULL)
268 (*chan->ops->close)(chan->fd, chan->data);
269 chan->opened = 0;
270 chan->fd = -1;
271 } 312 }
272} 313}
273 314
@@ -339,24 +380,27 @@ int chan_window_size(struct list_head *chans, unsigned short *rows_out,
339 return 0; 380 return 0;
340} 381}
341 382
342void free_one_chan(struct chan *chan) 383void free_one_chan(struct chan *chan, int delay_free_irq)
343{ 384{
344 list_del(&chan->list); 385 list_del(&chan->list);
386
387 close_one_chan(chan, delay_free_irq);
388
345 if(chan->ops->free != NULL) 389 if(chan->ops->free != NULL)
346 (*chan->ops->free)(chan->data); 390 (*chan->ops->free)(chan->data);
347 free_irq_by_fd(chan->fd); 391
348 if(chan->primary && chan->output) ignore_sigio_fd(chan->fd); 392 if(chan->primary && chan->output) ignore_sigio_fd(chan->fd);
349 kfree(chan); 393 kfree(chan);
350} 394}
351 395
352void free_chan(struct list_head *chans) 396void free_chan(struct list_head *chans, int delay_free_irq)
353{ 397{
354 struct list_head *ele, *next; 398 struct list_head *ele, *next;
355 struct chan *chan; 399 struct chan *chan;
356 400
357 list_for_each_safe(ele, next, chans){ 401 list_for_each_safe(ele, next, chans){
358 chan = list_entry(ele, struct chan, list); 402 chan = list_entry(ele, struct chan, list);
359 free_one_chan(chan); 403 free_one_chan(chan, delay_free_irq);
360 } 404 }
361} 405}
362 406
@@ -466,7 +510,8 @@ struct chan_type chan_table[] = {
466#endif 510#endif
467}; 511};
468 512
469static struct chan *parse_chan(char *str, int device, struct chan_opts *opts) 513static struct chan *parse_chan(struct line *line, char *str, int device,
514 struct chan_opts *opts)
470{ 515{
471 struct chan_type *entry; 516 struct chan_type *entry;
472 struct chan_ops *ops; 517 struct chan_ops *ops;
@@ -499,25 +544,30 @@ static struct chan *parse_chan(char *str, int device, struct chan_opts *opts)
499 if(chan == NULL) 544 if(chan == NULL)
500 return NULL; 545 return NULL;
501 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list), 546 *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
547 .free_list =
548 LIST_HEAD_INIT(chan->free_list),
549 .line = line,
502 .primary = 1, 550 .primary = 1,
503 .input = 0, 551 .input = 0,
504 .output = 0, 552 .output = 0,
505 .opened = 0, 553 .opened = 0,
554 .enabled = 0,
506 .fd = -1, 555 .fd = -1,
507 .ops = ops, 556 .ops = ops,
508 .data = data }); 557 .data = data });
509 return chan; 558 return chan;
510} 559}
511 560
512int parse_chan_pair(char *str, struct list_head *chans, int device, 561int parse_chan_pair(char *str, struct line *line, int device,
513 struct chan_opts *opts) 562 struct chan_opts *opts)
514{ 563{
564 struct list_head *chans = &line->chan_list;
515 struct chan *new, *chan; 565 struct chan *new, *chan;
516 char *in, *out; 566 char *in, *out;
517 567
518 if(!list_empty(chans)){ 568 if(!list_empty(chans)){
519 chan = list_entry(chans->next, struct chan, list); 569 chan = list_entry(chans->next, struct chan, list);
520 free_chan(chans); 570 free_chan(chans, 0);
521 INIT_LIST_HEAD(chans); 571 INIT_LIST_HEAD(chans);
522 } 572 }
523 573
@@ -526,14 +576,14 @@ int parse_chan_pair(char *str, struct list_head *chans, int device,
526 in = str; 576 in = str;
527 *out = '\0'; 577 *out = '\0';
528 out++; 578 out++;
529 new = parse_chan(in, device, opts); 579 new = parse_chan(line, in, device, opts);
530 if(new == NULL) 580 if(new == NULL)
531 return -1; 581 return -1;
532 582
533 new->input = 1; 583 new->input = 1;
534 list_add(&new->list, chans); 584 list_add(&new->list, chans);
535 585
536 new = parse_chan(out, device, opts); 586 new = parse_chan(line, out, device, opts);
537 if(new == NULL) 587 if(new == NULL)
538 return -1; 588 return -1;
539 589
@@ -541,7 +591,7 @@ int parse_chan_pair(char *str, struct list_head *chans, int device,
541 new->output = 1; 591 new->output = 1;
542 } 592 }
543 else { 593 else {
544 new = parse_chan(str, device, opts); 594 new = parse_chan(line, str, device, opts);
545 if(new == NULL) 595 if(new == NULL)
546 return -1; 596 return -1;
547 597
@@ -592,27 +642,12 @@ void chan_interrupt(struct list_head *chans, struct work_struct *task,
592 if(chan->primary){ 642 if(chan->primary){
593 if(tty != NULL) 643 if(tty != NULL)
594 tty_hangup(tty); 644 tty_hangup(tty);
595 line_disable(tty, irq); 645 close_chan(chans, 1);
596 close_chan(chans);
597 return; 646 return;
598 } 647 }
599 else { 648 else close_one_chan(chan, 1);
600 if(chan->ops->close != NULL)
601 chan->ops->close(chan->fd, chan->data);
602 }
603 } 649 }
604 } 650 }
605 out: 651 out:
606 if(tty) tty_flip_buffer_push(tty); 652 if(tty) tty_flip_buffer_push(tty);
607} 653}
608
609/*
610 * Overrides for Emacs so that we follow Linus's tabbing style.
611 * Emacs will notice this stuff at the end of the file and automatically
612 * adjust the settings for this buffer only. This must remain at the end
613 * of the file.
614 * ---------------------------------------------------------------------------
615 * Local variables:
616 * c-file-style: "linux"
617 * End:
618 */
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index da81d22ec04a..851a7c8caae5 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -1,4 +1,4 @@
1/* 1/*
2 * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) 2 * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL 3 * Licensed under the GPL
4 */ 4 */
@@ -23,8 +23,9 @@
23 23
24static irqreturn_t line_interrupt(int irq, void *data, struct pt_regs *unused) 24static irqreturn_t line_interrupt(int irq, void *data, struct pt_regs *unused)
25{ 25{
26 struct tty_struct *tty = data; 26 struct chan *chan = data;
27 struct line *line = tty->driver_data; 27 struct line *line = chan->line;
28 struct tty_struct *tty = line->tty;
28 29
29 if (line) 30 if (line)
30 chan_interrupt(&line->chan_list, &line->task, tty, irq); 31 chan_interrupt(&line->chan_list, &line->task, tty, irq);
@@ -33,10 +34,10 @@ static irqreturn_t line_interrupt(int irq, void *data, struct pt_regs *unused)
33 34
34static void line_timer_cb(void *arg) 35static void line_timer_cb(void *arg)
35{ 36{
36 struct tty_struct *tty = arg; 37 struct line *line = arg;
37 struct line *line = tty->driver_data;
38 38
39 line_interrupt(line->driver->read_irq, arg, NULL); 39 chan_interrupt(&line->chan_list, &line->task, line->tty,
40 line->driver->read_irq);
40} 41}
41 42
42/* Returns the free space inside the ring buffer of this line. 43/* Returns the free space inside the ring buffer of this line.
@@ -342,8 +343,9 @@ int line_ioctl(struct tty_struct *tty, struct file * file,
342static irqreturn_t line_write_interrupt(int irq, void *data, 343static irqreturn_t line_write_interrupt(int irq, void *data,
343 struct pt_regs *unused) 344 struct pt_regs *unused)
344{ 345{
345 struct tty_struct *tty = data; 346 struct chan *chan = data;
346 struct line *line = tty->driver_data; 347 struct line *line = chan->line;
348 struct tty_struct *tty = line->tty;
347 int err; 349 int err;
348 350
349 /* Interrupts are enabled here because we registered the interrupt with 351 /* Interrupts are enabled here because we registered the interrupt with
@@ -365,7 +367,7 @@ static irqreturn_t line_write_interrupt(int irq, void *data,
365 if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && 367 if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) &&
366 (tty->ldisc.write_wakeup != NULL)) 368 (tty->ldisc.write_wakeup != NULL))
367 (tty->ldisc.write_wakeup)(tty); 369 (tty->ldisc.write_wakeup)(tty);
368 370
369 /* BLOCKING mode 371 /* BLOCKING mode
370 * In blocking mode, everything sleeps on tty->write_wait. 372 * In blocking mode, everything sleeps on tty->write_wait.
371 * Sleeping in the console driver would break non-blocking 373 * Sleeping in the console driver would break non-blocking
@@ -377,52 +379,29 @@ static irqreturn_t line_write_interrupt(int irq, void *data,
377 return IRQ_HANDLED; 379 return IRQ_HANDLED;
378} 380}
379 381
380int line_setup_irq(int fd, int input, int output, struct tty_struct *tty) 382int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
381{ 383{
382 struct line *line = tty->driver_data;
383 struct line_driver *driver = line->driver; 384 struct line_driver *driver = line->driver;
384 int err = 0, flags = SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM; 385 int err = 0, flags = SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM;
385 386
386 if (input) 387 if (input)
387 err = um_request_irq(driver->read_irq, fd, IRQ_READ, 388 err = um_request_irq(driver->read_irq, fd, IRQ_READ,
388 line_interrupt, flags, 389 line_interrupt, flags,
389 driver->read_irq_name, tty); 390 driver->read_irq_name, data);
390 if (err) 391 if (err)
391 return err; 392 return err;
392 if (output) 393 if (output)
393 err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, 394 err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
394 line_write_interrupt, flags, 395 line_write_interrupt, flags,
395 driver->write_irq_name, tty); 396 driver->write_irq_name, data);
396 line->have_irq = 1; 397 line->have_irq = 1;
397 return err; 398 return err;
398} 399}
399 400
400void line_disable(struct tty_struct *tty, int current_irq)
401{
402 struct line *line = tty->driver_data;
403
404 if(!line->have_irq)
405 return;
406
407 if(line->driver->read_irq == current_irq)
408 free_irq_later(line->driver->read_irq, tty);
409 else {
410 free_irq(line->driver->read_irq, tty);
411 }
412
413 if(line->driver->write_irq == current_irq)
414 free_irq_later(line->driver->write_irq, tty);
415 else {
416 free_irq(line->driver->write_irq, tty);
417 }
418
419 line->have_irq = 0;
420}
421
422int line_open(struct line *lines, struct tty_struct *tty) 401int line_open(struct line *lines, struct tty_struct *tty)
423{ 402{
424 struct line *line; 403 struct line *line;
425 int err = 0; 404 int err = -ENODEV;
426 405
427 line = &lines[tty->index]; 406 line = &lines[tty->index];
428 tty->driver_data = line; 407 tty->driver_data = line;
@@ -430,29 +409,29 @@ int line_open(struct line *lines, struct tty_struct *tty)
430 /* The IRQ which takes this lock is not yet enabled and won't be run 409 /* The IRQ which takes this lock is not yet enabled and won't be run
431 * before the end, so we don't need to use spin_lock_irq.*/ 410 * before the end, so we don't need to use spin_lock_irq.*/
432 spin_lock(&line->lock); 411 spin_lock(&line->lock);
433 if (tty->count == 1) {
434 if (!line->valid) {
435 err = -ENODEV;
436 goto out;
437 }
438 412
439 err = open_chan(&line->chan_list); 413 tty->driver_data = line;
440 if(err) 414 line->tty = tty;
441 goto out; 415 if(!line->valid)
442 416 goto out;
443 /* Here the interrupt is registered.*/ 417
444 enable_chan(&line->chan_list, tty); 418 if(tty->count == 1){
445 INIT_WORK(&line->task, line_timer_cb, tty); 419 /* Here the device is opened, if necessary, and interrupt
446 } 420 * is registered.
421 */
422 enable_chan(line);
423 INIT_WORK(&line->task, line_timer_cb, line);
424
425 if(!line->sigio){
426 chan_enable_winch(&line->chan_list, tty);
427 line->sigio = 1;
428 }
447 429
448 if(!line->sigio){ 430 chan_window_size(&line->chan_list, &tty->winsize.ws_row,
449 chan_enable_winch(&line->chan_list, tty); 431 &tty->winsize.ws_col);
450 line->sigio = 1;
451 } 432 }
452 chan_window_size(&line->chan_list, &tty->winsize.ws_row,
453 &tty->winsize.ws_col);
454 line->count++;
455 433
434 err = 0;
456out: 435out:
457 spin_unlock(&line->lock); 436 spin_unlock(&line->lock);
458 return err; 437 return err;
@@ -472,15 +451,14 @@ void line_close(struct tty_struct *tty, struct file * filp)
472 /* We ignore the error anyway! */ 451 /* We ignore the error anyway! */
473 flush_buffer(line); 452 flush_buffer(line);
474 453
475 line->count--; 454 if(tty->count == 1){
476 if (tty->count == 1) { 455 line->tty = NULL;
477 line_disable(tty, -1);
478 tty->driver_data = NULL; 456 tty->driver_data = NULL;
479 }
480 457
481 if((line->count == 0) && line->sigio){ 458 if(line->sigio){
482 unregister_winch(tty); 459 unregister_winch(tty);
483 line->sigio = 0; 460 line->sigio = 0;
461 }
484 } 462 }
485 463
486 spin_unlock_irq(&line->lock); 464 spin_unlock_irq(&line->lock);
@@ -491,7 +469,7 @@ void close_lines(struct line *lines, int nlines)
491 int i; 469 int i;
492 470
493 for(i = 0; i < nlines; i++) 471 for(i = 0; i < nlines; i++)
494 close_chan(&lines[i].chan_list); 472 close_chan(&lines[i].chan_list, 0);
495} 473}
496 474
497/* Common setup code for both startup command line and mconsole initialization. 475/* Common setup code for both startup command line and mconsole initialization.
@@ -526,7 +504,7 @@ int line_setup(struct line *lines, unsigned int num, char *init)
526 return 0; 504 return 0;
527 } 505 }
528 else if (n >= 0){ 506 else if (n >= 0){
529 if (lines[n].count > 0) { 507 if (lines[n].tty != NULL) {
530 printk("line_setup - device %d is open\n", n); 508 printk("line_setup - device %d is open\n", n);
531 return 0; 509 return 0;
532 } 510 }
@@ -537,7 +515,7 @@ int line_setup(struct line *lines, unsigned int num, char *init)
537 else { 515 else {
538 lines[n].init_str = init; 516 lines[n].init_str = init;
539 lines[n].valid = 1; 517 lines[n].valid = 1;
540 } 518 }
541 } 519 }
542 } 520 }
543 else { 521 else {
@@ -578,7 +556,7 @@ int line_config(struct line *lines, unsigned int num, char *str,
578 return 1; 556 return 1;
579 557
580 line = &lines[n]; 558 line = &lines[n];
581 return parse_chan_pair(line->init_str, &line->chan_list, n, opts); 559 return parse_chan_pair(line->init_str, line, n, opts);
582} 560}
583 561
584int line_get_config(char *name, struct line *lines, unsigned int num, char *str, 562int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
@@ -604,7 +582,7 @@ int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
604 spin_lock(&line->lock); 582 spin_lock(&line->lock);
605 if(!line->valid) 583 if(!line->valid)
606 CONFIG_CHUNK(str, size, n, "none", 1); 584 CONFIG_CHUNK(str, size, n, "none", 1);
607 else if(line->count == 0) 585 else if(line->tty == NULL)
608 CONFIG_CHUNK(str, size, n, line->init_str, 1); 586 CONFIG_CHUNK(str, size, n, line->init_str, 1);
609 else n = chan_config_string(&line->chan_list, str, size, error_out); 587 else n = chan_config_string(&line->chan_list, str, size, error_out);
610 spin_unlock(&line->lock); 588 spin_unlock(&line->lock);
@@ -696,7 +674,7 @@ void lines_init(struct line *lines, int nlines, struct chan_opts *opts)
696 if(line->init_str == NULL) 674 if(line->init_str == NULL)
697 printk("lines_init - kstrdup returned NULL\n"); 675 printk("lines_init - kstrdup returned NULL\n");
698 676
699 if(parse_chan_pair(line->init_str, &line->chan_list, i, opts)){ 677 if(parse_chan_pair(line->init_str, line, i, opts)){
700 printk("parse_chan_pair failed for device %d\n", i); 678 printk("parse_chan_pair failed for device %d\n", i);
701 line->valid = 0; 679 line->valid = 0;
702 } 680 }
@@ -831,7 +809,7 @@ char *add_xterm_umid(char *base)
831 umid = get_umid(1); 809 umid = get_umid(1);
832 if(umid == NULL) 810 if(umid == NULL)
833 return base; 811 return base;
834 812
835 len = strlen(base) + strlen(" ()") + strlen(umid) + 1; 813 len = strlen(base) + strlen(" ()") + strlen(umid) + 1;
836 title = kmalloc(len, GFP_KERNEL); 814 title = kmalloc(len, GFP_KERNEL);
837 if(title == NULL){ 815 if(title == NULL){