This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / char / serial167.c
index c2deac9..885621e 100644 (file)
@@ -39,9 +39,6 @@
  * - don't use the panic function in serial167_init
  * - do resource release on failure on serial167_init
  * - include missing restore_flags in mvme167_serial_console_setup
- *
- * Kars de Jong <jongk@linux-m68k.org> - 2004/09/06
- * - replace bottom half handler with task queue handler
  */
 
 #include <linux/config.h>
 #include <linux/mm.h>
 #include <linux/console.h>
 #include <linux/module.h>
-#include <linux/bitops.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
+#include <asm/bitops.h>
 #include <asm/mvme16xhw.h>
 #include <asm/bootinfo.h>
 #include <asm/setup.h>
@@ -92,6 +89,8 @@
 
 #define SERIAL_TYPE_NORMAL  1
 
+DECLARE_TASK_QUEUE(tq_cyclades);
+
 static struct tty_driver *cy_serial_driver;
 extern int serial_console;
 static struct cyclades_port *serial_console_info = NULL;
@@ -374,7 +373,8 @@ static inline void
 cy_sched_event(struct cyclades_port *info, int event)
 {
     info->event |= 1 << event; /* remember what kind of event and who */
-    schedule_work(&info->tqueue);
+    queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */
+    mark_bh(CYCLADES_BH);                       /* then trigger event */
 } /* cy_sched_event */
 
 
@@ -467,7 +467,7 @@ cd2401_rxerr_interrupt(int irq, void *dev_id, struct pt_regs *fp)
               and nothing could be done about it!!! */
        }
     }
-    schedule_delayed_work(&tty->flip.work, 1);
+    queue_task(&tty->flip.tqueue, &tq_timer);
     /* end of service */
     base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
     return IRQ_HANDLED;
@@ -702,7 +702,7 @@ cd2401_rx_interrupt(int irq, void *dev_id, struct pt_regs *fp)
            udelay(10L);
 #endif
         }
-       schedule_delayed_work(&tty->flip.work, 1);
+       queue_task(&tty->flip.tqueue, &tq_timer);
     }
     /* end of service */
     base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS;
@@ -713,7 +713,7 @@ cd2401_rx_interrupt(int irq, void *dev_id, struct pt_regs *fp)
  * This routine is used to handle the "bottom half" processing for the
  * serial driver, known also the "software interrupt" processing.
  * This processing is done at the kernel interrupt level, after the
- * cy#/_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * cy_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
  * is where time-consuming activities which can not be done in the
  * interrupt driver proper are done; the interrupt driver schedules
  * them using cy_sched_event(), and they get done here.
@@ -721,7 +721,9 @@ cd2401_rx_interrupt(int irq, void *dev_id, struct pt_regs *fp)
  * This is done through one level of indirection--the task queue.
  * When a hardware interrupt service routine wants service by the
  * driver's bottom half, it enqueues the appropriate tq_struct (one
- * per port) to the keventd work queue and sets a request flag
+ * per port) to the tq_cyclades work queue and sets a request flag
+ * via mark_bh for processing that queue.  When the time is right,
+ * do_cyclades_bh is called (because of the mark_bh) and it requests
  * that the work queue be processed.
  *
  * Although this may seem unwieldy, it gives the system a way to
@@ -729,6 +731,12 @@ cd2401_rx_interrupt(int irq, void *dev_id, struct pt_regs *fp)
  * structure) to the bottom half of the driver.  Previous kernels
  * had to poll every port to see if that port needed servicing.
  */
+static void
+do_cyclades_bh(void)
+{
+    run_task_queue(&tq_cyclades);
+} /* do_cyclades_bh */
+
 static void
 do_softint(void *private_)
 {
@@ -1200,7 +1208,7 @@ cy_flush_chars(struct tty_struct *tty)
     port is already active, there is no need to kick it.
  */
 static int
-cy_write(struct tty_struct * tty,
+cy_write(struct tty_struct * tty, int from_user,
            const unsigned char *buf, int count)
 {
   struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
@@ -1219,23 +1227,53 @@ cy_write(struct tty_struct * tty,
         return 0;
     }
 
-    while (1) {
-           local_irq_save(flags);
-           c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-                                     SERIAL_XMIT_SIZE - info->xmit_head));
-           if (c <= 0) {
+    if (from_user) {
+           down(&tmp_buf_sem);
+           while (1) {
+                   c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                             SERIAL_XMIT_SIZE - info->xmit_head));
+                   if (c <= 0)
+                           break;
+
+                   c -= copy_from_user(tmp_buf, buf, c);
+                   if (!c) {
+                           if (!total)
+                                   total = -EFAULT;
+                           break;
+                   }
+
+                   local_irq_save(flags);
+                   c = min_t(int, c, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                         SERIAL_XMIT_SIZE - info->xmit_head));
+                   memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+                   info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+                   info->xmit_cnt += c;
                    local_irq_restore(flags);
-                   break;
+
+                   buf += c;
+                   count -= c;
+                   total += c;
            }
+           up(&tmp_buf_sem);
+    } else {
+           while (1) {
+                   local_irq_save(flags);
+                   c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                             SERIAL_XMIT_SIZE - info->xmit_head));
+                   if (c <= 0) {
+                           local_irq_restore(flags);
+                           break;
+                   }
 
-           memcpy(info->xmit_buf + info->xmit_head, buf, c);
-           info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
-           info->xmit_cnt += c;
-           local_irq_restore(flags);
+                   memcpy(info->xmit_buf + info->xmit_head, buf, c);
+                   info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+                   info->xmit_cnt += c;
+                   local_irq_restore(flags);
 
-           buf += c;
-           count -= c;
-           total += c;
+                   buf += c;
+                   count -= c;
+                   total += c;
+           }
     }
 
     if (info->xmit_cnt
@@ -1802,7 +1840,8 @@ cy_close(struct tty_struct * tty, struct file * filp)
     info->tty = 0;
     if (info->blocked_open) {
        if (info->close_delay) {
-           msleep_interruptible(jiffies_to_msecs(info->close_delay));
+           current->state = TASK_INTERRUPTIBLE;
+           schedule_timeout(info->close_delay);
        }
        wake_up_interruptible(&info->open_wait);
     }
@@ -2270,6 +2309,8 @@ scrn[1] = '\0';
            return ret;
     }
 
+    init_bh(CYCLADES_BH, do_cyclades_bh);
+
     port_num = 0;
     info = cy_port;
     for (index = 0; index < 1; index++) {
@@ -2307,7 +2348,8 @@ scrn[1] = '\0';
                info->blocked_open = 0;
                info->default_threshold = 0;
                info->default_timeout = 0;
-               INIT_WORK(&info->tqueue, do_softint, info);
+               info->tqueue.routine = do_softint;
+               info->tqueue.data = info;
                init_waitqueue_head(&info->open_wait);
                init_waitqueue_head(&info->close_wait);
                /* info->session */