This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / char / hvsi.c
index 35babe6..595079c 100644 (file)
  * the OS cannot change the speed of the port through this protocol.
  */
 
+/* TODO:
+ * test FSP reset
+ * add udbg support for xmon/kdb
+ */
+
 #undef DEBUG
 
 #include <linux/console.h>
@@ -49,7 +54,6 @@
 #include <asm/prom.h>
 #include <asm/uaccess.h>
 #include <asm/vio.h>
-#include <asm/param.h>
 
 #define HVSI_MAJOR     229
 #define HVSI_MINOR     128
@@ -70,7 +74,6 @@
 
 struct hvsi_struct {
        struct work_struct writer;
-       struct work_struct handshaker;
        wait_queue_head_t emptyq; /* woken when outbuf is emptied */
        wait_queue_head_t stateq; /* woken when HVSI state changes */
        spinlock_t lock;
@@ -106,7 +109,6 @@ enum HVSI_PROTOCOL_STATE {
        HVSI_WAIT_FOR_VER_QUERY,
        HVSI_OPEN,
        HVSI_WAIT_FOR_MCTRL_RESPONSE,
-       HVSI_FSP_DIED,
 };
 #define HVSI_CONSOLE 0x1
 
@@ -170,13 +172,6 @@ struct hvsi_query_response {
        } u;
 } __attribute__((packed));
 
-
-
-static inline int is_console(struct hvsi_struct *hp)
-{
-       return hp->flags & HVSI_CONSOLE;
-}
-
 static inline int is_open(struct hvsi_struct *hp)
 {
        /* if we're waiting for an mctrl then we're already open */
@@ -193,7 +188,6 @@ static inline void print_state(struct hvsi_struct *hp)
                "HVSI_WAIT_FOR_VER_QUERY",
                "HVSI_OPEN",
                "HVSI_WAIT_FOR_MCTRL_RESPONSE",
-               "HVSI_FSP_DIED",
        };
        const char *name = state_names[hp->state];
 
@@ -302,9 +296,14 @@ static int hvsi_read(struct hvsi_struct *hp, char *buf, int count)
        return 0;
 }
 
-static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet,
-       struct tty_struct **to_hangup, struct hvsi_struct **to_handshake)
+/*
+ * we can't call tty_hangup() directly here because we need to call that
+ * outside of our lock
+ */
+static struct tty_struct *hvsi_recv_control(struct hvsi_struct *hp,
+               uint8_t *packet)
 {
+       struct tty_struct *to_hangup = NULL;
        struct hvsi_control *header = (struct hvsi_control *)packet;
 
        switch (header->verb) {
@@ -314,14 +313,15 @@ static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet,
                                pr_debug("hvsi%i: CD dropped\n", hp->index);
                                hp->mctrl &= TIOCM_CD;
                                if (!(hp->tty->flags & CLOCAL))
-                                       *to_hangup = hp->tty;
+                                       to_hangup = hp->tty;
                        }
                        break;
                case VSV_CLOSE_PROTOCOL:
-                       pr_debug("hvsi%i: service processor came back\n", hp->index);
-                       if (hp->state != HVSI_CLOSED) {
-                               *to_handshake = hp;
-                       }
+                       printk(KERN_DEBUG
+                               "hvsi%i: service processor closed connection!\n", hp->index);
+                       __set_state(hp, HVSI_CLOSED);
+                       to_hangup = hp->tty;
+                       hp->tty = NULL;
                        break;
                default:
                        printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ",
@@ -329,6 +329,8 @@ static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet,
                        dump_packet(packet);
                        break;
        }
+
+       return to_hangup;
 }
 
 static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet)
@@ -386,8 +388,8 @@ static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet)
 
        switch (hp->state) {
                case HVSI_WAIT_FOR_VER_QUERY:
-                       hvsi_version_respond(hp, query->seqno);
                        __set_state(hp, HVSI_OPEN);
+                       hvsi_version_respond(hp, query->seqno);
                        break;
                default:
                        printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index);
@@ -465,20 +467,17 @@ static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp,
  * incoming data).
  */
 static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip,
-               struct tty_struct **hangup, struct hvsi_struct **handshake)
+               struct tty_struct **hangup)
 {
        uint8_t *packet = hp->inbuf;
        int chunklen;
 
        *flip = NULL;
        *hangup = NULL;
-       *handshake = NULL;
 
        chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ);
-       if (chunklen == 0) {
-               pr_debug("%s: 0-length read\n", __FUNCTION__);
+       if (chunklen == 0)
                return 0;
-       }
 
        pr_debug("%s: got %i bytes\n", __FUNCTION__, chunklen);
        dbg_dump_hex(hp->inbuf_end, chunklen);
@@ -510,7 +509,7 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip,
                                *flip = hvsi_recv_data(hp, packet);
                                break;
                        case VS_CONTROL_PACKET_HEADER:
-                               hvsi_recv_control(hp, packet, hangup, handshake);
+                               *hangup = hvsi_recv_control(hp, packet);
                                break;
                        case VS_QUERY_RESPONSE_PACKET_HEADER:
                                hvsi_recv_response(hp, packet);
@@ -527,8 +526,8 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip,
 
                packet += len_packet(packet);
 
-               if (*hangup || *handshake) {
-                       pr_debug("%s: hangup or handshake\n", __FUNCTION__);
+               if (*hangup) {
+                       pr_debug("%s: hangup\n", __FUNCTION__);
                        /*
                         * we need to send the hangup now before receiving any more data.
                         * If we get "data, hangup, data", we can't deliver the second
@@ -561,15 +560,16 @@ static irqreturn_t hvsi_interrupt(int irq, void *arg, struct pt_regs *regs)
        struct hvsi_struct *hp = (struct hvsi_struct *)arg;
        struct tty_struct *flip;
        struct tty_struct *hangup;
-       struct hvsi_struct *handshake;
        unsigned long flags;
+       irqreturn_t handled = IRQ_NONE;
        int again = 1;
 
        pr_debug("%s\n", __FUNCTION__);
 
        while (again) {
                spin_lock_irqsave(&hp->lock, flags);
-               again = hvsi_load_chunk(hp, &flip, &hangup, &handshake);
+               again = hvsi_load_chunk(hp, &flip, &hangup);
+               handled = IRQ_HANDLED;
                spin_unlock_irqrestore(&hp->lock, flags);
 
                /*
@@ -587,11 +587,6 @@ static irqreturn_t hvsi_interrupt(int irq, void *arg, struct pt_regs *regs)
                if (hangup) {
                        tty_hangup(hangup);
                }
-
-               if (handshake) {
-                       pr_debug("hvsi%i: attempting re-handshake\n", handshake->index);
-                       schedule_work(&handshake->handshaker);
-               }
        }
 
        spin_lock_irqsave(&hp->lock, flags);
@@ -608,7 +603,7 @@ static irqreturn_t hvsi_interrupt(int irq, void *arg, struct pt_regs *regs)
                tty_flip_buffer_push(flip);
        }
 
-       return IRQ_HANDLED;
+       return handled;
 }
 
 /* for boot console, before the irq handler is running */
@@ -762,23 +757,6 @@ static int hvsi_handshake(struct hvsi_struct *hp)
        return 0;
 }
 
-static void hvsi_handshaker(void *arg)
-{
-       struct hvsi_struct *hp = (struct hvsi_struct *)arg;
-
-       if (hvsi_handshake(hp) >= 0)
-               return;
-
-       printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index);
-       if (is_console(hp)) {
-               /*
-                * ttys will re-attempt the handshake via hvsi_open, but
-                * the console will not.
-                */
-               printk(KERN_ERR "hvsi%i: lost console!\n", hp->index);
-       }
-}
-
 static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count)
 {
        struct hvsi_data packet __ALIGNED__;
@@ -830,10 +808,6 @@ static int hvsi_open(struct tty_struct *tty, struct file *filp)
        tty->driver_data = hp;
        tty->low_latency = 1; /* avoid throttle/tty_flip_buffer_push race */
 
-       mb();
-       if (hp->state == HVSI_FSP_DIED)
-               return -EIO;
-
        spin_lock_irqsave(&hp->lock, flags);
        hp->tty = tty;
        hp->count++;
@@ -841,7 +815,7 @@ static int hvsi_open(struct tty_struct *tty, struct file *filp)
        h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE);
        spin_unlock_irqrestore(&hp->lock, flags);
 
-       if (is_console(hp))
+       if (hp->flags & HVSI_CONSOLE)
                return 0; /* this has already been handshaked as the console */
 
        ret = hvsi_handshake(hp);
@@ -915,7 +889,7 @@ static void hvsi_close(struct tty_struct *tty, struct file *filp)
                hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */
 
                /* only close down connection if it is not the console */
-               if (!is_console(hp)) {
+               if (!(hp->flags & HVSI_CONSOLE)) {
                        h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */
                        __set_state(hp, HVSI_CLOSED);
                        /*
@@ -953,17 +927,11 @@ static void hvsi_close(struct tty_struct *tty, struct file *filp)
 static void hvsi_hangup(struct tty_struct *tty)
 {
        struct hvsi_struct *hp = tty->driver_data;
-       unsigned long flags;
 
        pr_debug("%s\n", __FUNCTION__);
 
-       spin_lock_irqsave(&hp->lock, flags);
-
        hp->count = 0;
-       hp->n_outbuf = 0;
        hp->tty = NULL;
-
-       spin_unlock_irqrestore(&hp->lock, flags);
 }
 
 /* called with hp->lock held */
@@ -975,13 +943,12 @@ static void hvsi_push(struct hvsi_struct *hp)
                return;
 
        n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf);
-       if (n > 0) {
-               /* success */
-               pr_debug("%s: wrote %i chars\n", __FUNCTION__, n);
+       if (n != 0) {
+               /*
+                * either all data was sent or there was an error, and we throw away
+                * data on error.
+                */
                hp->n_outbuf = 0;
-       } else if (n == -EIO) {
-               __set_state(hp, HVSI_FSP_DIED);
-               printk(KERN_ERR "hvsi%i: service processor died\n", hp->index);
        }
 }
 
@@ -999,19 +966,6 @@ static void hvsi_write_worker(void *arg)
 
        spin_lock_irqsave(&hp->lock, flags);
 
-       pr_debug("%s: %i chars in buffer\n", __FUNCTION__, hp->n_outbuf);
-
-       if (!is_open(hp)) {
-               /*
-                * We could have a non-open connection if the service processor died
-                * while we were busily scheduling ourselves. In that case, it could
-                * be minutes before the service processor comes back, so only try
-                * again once a second.
-                */
-               schedule_delayed_work(&hp->writer, HZ);
-               goto out;
-       }
-
        hvsi_push(hp);
        if (hp->n_outbuf > 0)
                schedule_delayed_work(&hp->writer, 10);
@@ -1028,7 +982,6 @@ static void hvsi_write_worker(void *arg)
                wake_up_interruptible(&hp->tty->write_wait);
        }
 
-out:
        spin_unlock_irqrestore(&hp->lock, flags);
 }
 
@@ -1046,18 +999,28 @@ static int hvsi_chars_in_buffer(struct tty_struct *tty)
        return hp->n_outbuf;
 }
 
-static int hvsi_write(struct tty_struct *tty,
+static int hvsi_write(struct tty_struct *tty, int from_user,
                     const unsigned char *buf, int count)
 {
        struct hvsi_struct *hp = tty->driver_data;
        const char *source = buf;
+       char *kbuf = NULL;
        unsigned long flags;
        int total = 0;
        int origcount = count;
 
-       spin_lock_irqsave(&hp->lock, flags);
+       if (from_user) {
+               kbuf = kmalloc(count, GFP_KERNEL);
+               if (kbuf == NULL)
+                       return -ENOMEM;
+               if (copy_from_user(kbuf, buf, count)) {
+                       kfree(kbuf);
+                       return -EFAULT;
+               }
+               source = kbuf;
+       }
 
-       pr_debug("%s: %i chars in buffer\n", __FUNCTION__, hp->n_outbuf);
+       spin_lock_irqsave(&hp->lock, flags);
 
        if (!is_open(hp)) {
                /* we're either closing or not yet open; don't accept data */
@@ -1094,6 +1057,9 @@ static int hvsi_write(struct tty_struct *tty,
 out:
        spin_unlock_irqrestore(&hp->lock, flags);
 
+       if (from_user)
+               kfree(kbuf);
+
        if (total != origcount)
                pr_debug("%s: wanted %i, only wrote %i\n", __FUNCTION__, origcount,
                        total);
@@ -1328,7 +1294,6 @@ static int __init hvsi_console_init(void)
 
                hp = &hvsi_ports[hvsi_count];
                INIT_WORK(&hp->writer, hvsi_write_worker, hp);
-               INIT_WORK(&hp->handshaker, hvsi_handshaker, hp);
                init_waitqueue_head(&hp->emptyq);
                init_waitqueue_head(&hp->stateq);
                hp->lock = SPIN_LOCK_UNLOCKED;