Merge branch 'irqclean-submit1' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzi...
[pandora-kernel.git] / arch / um / drivers / chan_kern.c
index 59c9b3f..3576b3c 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
  * Licensed under the GPL
  */
@@ -110,7 +110,7 @@ static void not_configged_free(void *data)
               "UML\n");
 }
 
-static struct chan_ops not_configged_ops = {
+static const struct chan_ops not_configged_ops = {
        .init           = not_configged_init,
        .open           = not_configged_open,
        .close          = not_configged_close,
@@ -186,9 +186,6 @@ static void tty_receive_char(struct tty_struct *tty, char ch)
                }
        }
 
-       if((tty->flip.flag_buf_ptr == NULL) ||
-          (tty->flip.char_buf_ptr == NULL))
-               return;
        tty_insert_flip_char(tty, ch, TTY_NORMAL);
 }
 
@@ -240,20 +237,65 @@ void chan_enable_winch(struct list_head *chans, struct tty_struct *tty)
        }
 }
 
-void enable_chan(struct list_head *chans, struct tty_struct *tty)
+void enable_chan(struct line *line)
 {
        struct list_head *ele;
        struct chan *chan;
 
-       list_for_each(ele, chans){
+       list_for_each(ele, &line->chan_list){
                chan = list_entry(ele, struct chan, list);
-               if(!chan->opened) continue;
+               if(open_one_chan(chan))
+                       continue;
+
+               if(chan->enabled)
+                       continue;
+               line_setup_irq(chan->fd, chan->input, chan->output, line,
+                              chan);
+               chan->enabled = 1;
+       }
+}
+
+static LIST_HEAD(irqs_to_free);
+
+void free_irqs(void)
+{
+       struct chan *chan;
+
+       while(!list_empty(&irqs_to_free)){
+               chan = list_entry(irqs_to_free.next, struct chan, free_list);
+               list_del(&chan->free_list);
+
+               if(chan->input)
+                       free_irq(chan->line->driver->read_irq, chan);
+               if(chan->output)
+                       free_irq(chan->line->driver->write_irq, chan);
+               chan->enabled = 0;
+       }
+}
+
+static void close_one_chan(struct chan *chan, int delay_free_irq)
+{
+       if(!chan->opened)
+               return;
 
-               line_setup_irq(chan->fd, chan->input, chan->output, tty);
+       if(delay_free_irq){
+               list_add(&chan->free_list, &irqs_to_free);
        }
+       else {
+               if(chan->input)
+                       free_irq(chan->line->driver->read_irq, chan);
+               if(chan->output)
+                       free_irq(chan->line->driver->write_irq, chan);
+               chan->enabled = 0;
+       }
+       if(chan->ops->close != NULL)
+               (*chan->ops->close)(chan->fd, chan->data);
+
+       chan->opened = 0;
+       chan->fd = -1;
 }
 
-void close_chan(struct list_head *chans)
+void close_chan(struct list_head *chans, int delay_free_irq)
 {
        struct chan *chan;
 
@@ -263,11 +305,33 @@ void close_chan(struct list_head *chans)
         * so it must be the last closed.
         */
        list_for_each_entry_reverse(chan, chans, list) {
-               if(!chan->opened) continue;
-               if(chan->ops->close != NULL)
-                       (*chan->ops->close)(chan->fd, chan->data);
-               chan->opened = 0;
-               chan->fd = -1;
+               close_one_chan(chan, delay_free_irq);
+       }
+}
+
+void deactivate_chan(struct list_head *chans, int irq)
+{
+       struct list_head *ele;
+
+       struct chan *chan;
+       list_for_each(ele, chans) {
+               chan = list_entry(ele, struct chan, list);
+
+               if(chan->enabled && chan->input)
+                       deactivate_fd(chan->fd, irq);
+       }
+}
+
+void reactivate_chan(struct list_head *chans, int irq)
+{
+       struct list_head *ele;
+       struct chan *chan;
+
+       list_for_each(ele, chans) {
+               chan = list_entry(ele, struct chan, list);
+
+               if(chan->enabled && chan->input)
+                       reactivate_fd(chan->fd, irq);
        }
 }
 
@@ -309,16 +373,14 @@ int console_write_chan(struct list_head *chans, const char *buf, int len)
 }
 
 int console_open_chan(struct line *line, struct console *co,
-                     struct chan_opts *opts)
+                     const struct chan_opts *opts)
 {
-       if (!list_empty(&line->chan_list))
-               return 0;
+       int err;
+
+       err = open_chan(&line->chan_list);
+       if(err)
+               return err;
 
-       if (0 != parse_chan_pair(line->init_str, &line->chan_list,
-                                co->index, opts))
-               return -1;
-       if (0 != open_chan(&line->chan_list))
-               return -1;
        printk("Console initialized on /dev/%s%d\n",co->name,co->index);
        return 0;
 }
@@ -341,24 +403,27 @@ int chan_window_size(struct list_head *chans, unsigned short *rows_out,
        return 0;
 }
 
-void free_one_chan(struct chan *chan)
+static void free_one_chan(struct chan *chan, int delay_free_irq)
 {
        list_del(&chan->list);
+
+       close_one_chan(chan, delay_free_irq);
+
        if(chan->ops->free != NULL)
                (*chan->ops->free)(chan->data);
-       free_irq_by_fd(chan->fd);
+
        if(chan->primary && chan->output) ignore_sigio_fd(chan->fd);
        kfree(chan);
 }
 
-void free_chan(struct list_head *chans)
+static void free_chan(struct list_head *chans, int delay_free_irq)
 {
        struct list_head *ele, *next;
        struct chan *chan;
 
        list_for_each_safe(ele, next, chans){
                chan = list_entry(ele, struct chan, list);
-               free_one_chan(chan);
+               free_one_chan(chan, delay_free_irq);
        }
 }
 
@@ -429,10 +494,10 @@ int chan_config_string(struct list_head *chans, char *str, int size,
 
 struct chan_type {
        char *key;
-       struct chan_ops *ops;
+       const struct chan_ops *ops;
 };
 
-struct chan_type chan_table[] = {
+static const struct chan_type chan_table[] = {
        { "fd", &fd_ops },
 
 #ifdef CONFIG_NULL_CHAN
@@ -468,17 +533,18 @@ struct chan_type chan_table[] = {
 #endif
 };
 
-static struct chan *parse_chan(char *str, int device, struct chan_opts *opts)
+static struct chan *parse_chan(struct line *line, char *str, int device,
+                              const struct chan_opts *opts)
 {
-       struct chan_type *entry;
-       struct chan_ops *ops;
+       const struct chan_type *entry;
+       const struct chan_ops *ops;
        struct chan *chan;
        void *data;
        int i;
 
        ops = NULL;
        data = NULL;
-       for(i = 0; i < sizeof(chan_table)/sizeof(chan_table[0]); i++){
+       for(i = 0; i < ARRAY_SIZE(chan_table); i++){
                entry = &chan_table[i];
                if(!strncmp(str, entry->key, strlen(entry->key))){
                        ops = entry->ops;
@@ -501,25 +567,30 @@ static struct chan *parse_chan(char *str, int device, struct chan_opts *opts)
        if(chan == NULL)
                return NULL;
        *chan = ((struct chan) { .list          = LIST_HEAD_INIT(chan->list),
+                                .free_list     =
+                                       LIST_HEAD_INIT(chan->free_list),
+                                .line          = line,
                                 .primary       = 1,
                                 .input         = 0,
                                 .output        = 0,
                                 .opened        = 0,
+                                .enabled       = 0,
                                 .fd            = -1,
                                 .ops           = ops,
                                 .data          = data });
        return chan;
 }
 
-int parse_chan_pair(char *str, struct list_head *chans, int device,
-                   struct chan_opts *opts)
+int parse_chan_pair(char *str, struct line *line, int device,
+                   const struct chan_opts *opts)
 {
+       struct list_head *chans = &line->chan_list;
        struct chan *new, *chan;
        char *in, *out;
 
        if(!list_empty(chans)){
                chan = list_entry(chans->next, struct chan, list);
-               free_chan(chans);
+               free_chan(chans, 0);
                INIT_LIST_HEAD(chans);
        }
 
@@ -528,14 +599,14 @@ int parse_chan_pair(char *str, struct list_head *chans, int device,
                in = str;
                *out = '\0';
                out++;
-               new = parse_chan(in, device, opts);
+               new = parse_chan(line, in, device, opts);
                if(new == NULL)
                        return -1;
 
                new->input = 1;
                list_add(&new->list, chans);
 
-               new = parse_chan(out, device, opts);
+               new = parse_chan(line, out, device, opts);
                if(new == NULL)
                        return -1;
 
@@ -543,7 +614,7 @@ int parse_chan_pair(char *str, struct list_head *chans, int device,
                new->output = 1;
        }
        else {
-               new = parse_chan(str, device, opts);
+               new = parse_chan(line, str, device, opts);
                if(new == NULL)
                        return -1;
 
@@ -579,9 +650,8 @@ void chan_interrupt(struct list_head *chans, struct work_struct *task,
                chan = list_entry(ele, struct chan, list);
                if(!chan->input || (chan->ops->read == NULL)) continue;
                do {
-                       if((tty != NULL) &&
-                          (tty->flip.count >= TTY_FLIPBUF_SIZE)){
-                               schedule_work(task);
+                       if (tty && !tty_buffer_request_room(tty, 1)) {
+                               schedule_delayed_work(task, 1);
                                goto out;
                        }
                        err = chan->ops->read(chan->fd, &c, chan->data);
@@ -594,29 +664,12 @@ void chan_interrupt(struct list_head *chans, struct work_struct *task,
                        if(chan->primary){
                                if(tty != NULL)
                                        tty_hangup(tty);
-                               line_disable(tty, irq);
-                               close_chan(chans);
-                               free_chan(chans);
+                               close_chan(chans, 1);
                                return;
                        }
-                       else {
-                               if(chan->ops->close != NULL)
-                                       chan->ops->close(chan->fd, chan->data);
-                               free_one_chan(chan);
-                       }
+                       else close_one_chan(chan, 1);
                }
        }
  out:
        if(tty) tty_flip_buffer_push(tty);
 }
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */