root/drivers/tty/serial/jsm/jsm_tty.c
// SPDX-License-Identifier: GPL-2.0+
/************************************************************************
 * Copyright 2003 Digi International (www.digi.com)
 *
 * Copyright (C) 2004 IBM Corporation. All rights reserved.
 *
 * Contact Information:
 * Scott H Kilau <Scott_Kilau@digi.com>
 * Ananda Venkatarman <mansarov@us.ibm.com>
 * Modifications:
 * 01/19/06:    changed jsm_input routine to use the dynamically allocated
 *              tty_buffer changes. Contributors: Scott Kilau and Ananda V.
 ***********************************************************************/
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_reg.h>
#include <linux/delay.h>        /* For udelay */
#include <linux/pci.h>
#include <linux/slab.h>

#include "jsm.h"

static DECLARE_BITMAP(linemap, MAXLINES);

static void jsm_carrier(struct jsm_channel *ch);

static inline int jsm_get_mstat(struct jsm_channel *ch)
{
        unsigned char mstat;
        int result;

        jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "start\n");

        mstat = (ch->ch_mostat | ch->ch_mistat);

        result = 0;

        if (mstat & UART_MCR_DTR)
                result |= TIOCM_DTR;
        if (mstat & UART_MCR_RTS)
                result |= TIOCM_RTS;
        if (mstat & UART_MSR_CTS)
                result |= TIOCM_CTS;
        if (mstat & UART_MSR_DSR)
                result |= TIOCM_DSR;
        if (mstat & UART_MSR_RI)
                result |= TIOCM_RI;
        if (mstat & UART_MSR_DCD)
                result |= TIOCM_CD;

        jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n");
        return result;
}

static unsigned int jsm_tty_tx_empty(struct uart_port *port)
{
        return TIOCSER_TEMT;
}

/*
 * Return modem signals to ld.
 */
static unsigned int jsm_tty_get_mctrl(struct uart_port *port)
{
        int result;
        struct jsm_channel *channel =
                container_of(port, struct jsm_channel, uart_port);

        jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");

        result = jsm_get_mstat(channel);

        if (result < 0)
                return -ENXIO;

        jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");

        return result;
}

/*
 * jsm_set_modem_info()
 *
 * Set modem signals, called by ld.
 */
static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
        struct jsm_channel *channel =
                container_of(port, struct jsm_channel, uart_port);

        jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");

        if (mctrl & TIOCM_RTS)
                channel->ch_mostat |= UART_MCR_RTS;
        else
                channel->ch_mostat &= ~UART_MCR_RTS;

        if (mctrl & TIOCM_DTR)
                channel->ch_mostat |= UART_MCR_DTR;
        else
                channel->ch_mostat &= ~UART_MCR_DTR;

        channel->ch_bd->bd_ops->assert_modem_signals(channel);

        jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");
        udelay(10);
}

/*
 * jsm_tty_write()
 *
 * Take data from the user or kernel and send it out to the FEP.
 * In here exists all the Transparent Print magic as well.
 */
static void jsm_tty_write(struct uart_port *port)
{
        struct jsm_channel *channel;

        channel = container_of(port, struct jsm_channel, uart_port);
        channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel);
}

static void jsm_tty_start_tx(struct uart_port *port)
{
        struct jsm_channel *channel =
                container_of(port, struct jsm_channel, uart_port);

        jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");

        channel->ch_flags &= ~(CH_STOP);
        jsm_tty_write(port);

        jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");
}

static void jsm_tty_stop_tx(struct uart_port *port)
{
        struct jsm_channel *channel =
                container_of(port, struct jsm_channel, uart_port);

        jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "start\n");

        channel->ch_flags |= (CH_STOP);

        jsm_dbg(IOCTL, &channel->ch_bd->pci_dev, "finish\n");
}

static void jsm_tty_send_xchar(struct uart_port *port, char ch)
{
        unsigned long lock_flags;
        struct jsm_channel *channel =
                container_of(port, struct jsm_channel, uart_port);
        struct ktermios *termios;

        uart_port_lock_irqsave(port, &lock_flags);
        termios = &port->state->port.tty->termios;
        if (ch == termios->c_cc[VSTART])
                channel->ch_bd->bd_ops->send_start_character(channel);

        if (ch == termios->c_cc[VSTOP])
                channel->ch_bd->bd_ops->send_stop_character(channel);
        uart_port_unlock_irqrestore(port, lock_flags);
}

static void jsm_tty_stop_rx(struct uart_port *port)
{
        struct jsm_channel *channel =
                container_of(port, struct jsm_channel, uart_port);

        channel->ch_bd->bd_ops->disable_receiver(channel);
}

static void jsm_tty_break(struct uart_port *port, int break_state)
{
        unsigned long lock_flags;
        struct jsm_channel *channel =
                container_of(port, struct jsm_channel, uart_port);

        uart_port_lock_irqsave(port, &lock_flags);
        if (break_state == -1)
                channel->ch_bd->bd_ops->send_break(channel);
        else
                channel->ch_bd->bd_ops->clear_break(channel);

        uart_port_unlock_irqrestore(port, lock_flags);
}

static int jsm_tty_open(struct uart_port *port)
{
        unsigned long lock_flags;
        struct jsm_board *brd;
        struct jsm_channel *channel =
                container_of(port, struct jsm_channel, uart_port);
        struct ktermios *termios;

        /* Get board pointer from our array of majors we have allocated */
        brd = channel->ch_bd;

        /*
         * Allocate channel buffers for read/write/error.
         * Set flag, so we don't get trounced on.
         */
        channel->ch_flags |= (CH_OPENING);

        /* Drop locks, as malloc with GFP_KERNEL can sleep */

        if (!channel->ch_rqueue) {
                channel->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL);
                if (!channel->ch_rqueue) {
                        jsm_dbg(INIT, &channel->ch_bd->pci_dev,
                                "unable to allocate read queue buf\n");
                        return -ENOMEM;
                }
        }
        if (!channel->ch_equeue) {
                channel->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL);
                if (!channel->ch_equeue) {
                        jsm_dbg(INIT, &channel->ch_bd->pci_dev,
                                "unable to allocate error queue buf\n");
                        return -ENOMEM;
                }
        }

        channel->ch_flags &= ~(CH_OPENING);
        /*
         * Initialize if neither terminal is open.
         */
        jsm_dbg(OPEN, &channel->ch_bd->pci_dev,
                "jsm_open: initializing channel in open...\n");

        /*
         * Flush input queues.
         */
        channel->ch_r_head = channel->ch_r_tail = 0;
        channel->ch_e_head = channel->ch_e_tail = 0;

        brd->bd_ops->flush_uart_write(channel);
        brd->bd_ops->flush_uart_read(channel);

        channel->ch_flags = 0;
        channel->ch_cached_lsr = 0;
        channel->ch_stops_sent = 0;

        uart_port_lock_irqsave(port, &lock_flags);
        termios = &port->state->port.tty->termios;
        channel->ch_c_cflag     = termios->c_cflag;
        channel->ch_c_iflag     = termios->c_iflag;
        channel->ch_c_oflag     = termios->c_oflag;
        channel->ch_c_lflag     = termios->c_lflag;
        channel->ch_startc      = termios->c_cc[VSTART];
        channel->ch_stopc       = termios->c_cc[VSTOP];

        /* Tell UART to init itself */
        brd->bd_ops->uart_init(channel);

        /*
         * Run param in case we changed anything
         */
        brd->bd_ops->param(channel);

        jsm_carrier(channel);

        channel->ch_open_count++;
        uart_port_unlock_irqrestore(port, lock_flags);

        jsm_dbg(OPEN, &channel->ch_bd->pci_dev, "finish\n");
        return 0;
}

static void jsm_tty_close(struct uart_port *port)
{
        struct jsm_board *bd;
        struct jsm_channel *channel =
                container_of(port, struct jsm_channel, uart_port);

        jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "start\n");

        bd = channel->ch_bd;

        channel->ch_flags &= ~(CH_STOPI);

        channel->ch_open_count--;

        /*
         * If we have HUPCL set, lower DTR and RTS
         */
        if (channel->ch_c_cflag & HUPCL) {
                jsm_dbg(CLOSE, &channel->ch_bd->pci_dev,
                        "Close. HUPCL set, dropping DTR/RTS\n");

                /* Drop RTS/DTR */
                channel->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS);
                bd->bd_ops->assert_modem_signals(channel);
        }

        /* Turn off UART interrupts for this port */
        channel->ch_bd->bd_ops->uart_off(channel);

        jsm_dbg(CLOSE, &channel->ch_bd->pci_dev, "finish\n");
}

static void jsm_tty_set_termios(struct uart_port *port,
                                struct ktermios *termios,
                                const struct ktermios *old_termios)
{
        unsigned long lock_flags;
        struct jsm_channel *channel =
                container_of(port, struct jsm_channel, uart_port);

        uart_port_lock_irqsave(port, &lock_flags);
        channel->ch_c_cflag     = termios->c_cflag;
        channel->ch_c_iflag     = termios->c_iflag;
        channel->ch_c_oflag     = termios->c_oflag;
        channel->ch_c_lflag     = termios->c_lflag;
        channel->ch_startc      = termios->c_cc[VSTART];
        channel->ch_stopc       = termios->c_cc[VSTOP];

        channel->ch_bd->bd_ops->param(channel);
        jsm_carrier(channel);
        uart_port_unlock_irqrestore(port, lock_flags);
}

static const char *jsm_tty_type(struct uart_port *port)
{
        return "jsm";
}

static void jsm_tty_release_port(struct uart_port *port)
{
}

static int jsm_tty_request_port(struct uart_port *port)
{
        return 0;
}

static void jsm_config_port(struct uart_port *port, int flags)
{
        port->type = PORT_JSM;
}

static const struct uart_ops jsm_ops = {
        .tx_empty       = jsm_tty_tx_empty,
        .set_mctrl      = jsm_tty_set_mctrl,
        .get_mctrl      = jsm_tty_get_mctrl,
        .stop_tx        = jsm_tty_stop_tx,
        .start_tx       = jsm_tty_start_tx,
        .send_xchar     = jsm_tty_send_xchar,
        .stop_rx        = jsm_tty_stop_rx,
        .break_ctl      = jsm_tty_break,
        .startup        = jsm_tty_open,
        .shutdown       = jsm_tty_close,
        .set_termios    = jsm_tty_set_termios,
        .type           = jsm_tty_type,
        .release_port   = jsm_tty_release_port,
        .request_port   = jsm_tty_request_port,
        .config_port    = jsm_config_port,
};

/*
 * jsm_tty_init()
 *
 * Init the tty subsystem.  Called once per board after board has been
 * downloaded and init'ed.
 */
int jsm_tty_init(struct jsm_board *brd)
{
        int i;
        void __iomem *vaddr;
        struct jsm_channel *ch;

        if (!brd)
                return -ENXIO;

        jsm_dbg(INIT, &brd->pci_dev, "start\n");

        /*
         * Initialize board structure elements.
         */

        brd->nasync = brd->maxports;

        /*
         * Allocate channel memory that might not have been allocated
         * when the driver was first loaded.
         */
        for (i = 0; i < brd->nasync; i++) {
                if (!brd->channels[i]) {

                        /*
                         * Okay to malloc with GFP_KERNEL, we are not at
                         * interrupt context, and there are no locks held.
                         */
                        brd->channels[i] = kzalloc_obj(struct jsm_channel);
                        if (!brd->channels[i]) {
                                jsm_dbg(CORE, &brd->pci_dev,
                                        "%s:%d Unable to allocate memory for channel struct\n",
                                        __FILE__, __LINE__);
                        }
                }
        }

        ch = brd->channels[0];
        vaddr = brd->re_map_membase;

        /* Set up channel variables */
        for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {

                if (!brd->channels[i])
                        continue;

                spin_lock_init(&ch->ch_lock);

                if (brd->bd_uart_offset == 0x200)
                        ch->ch_neo_uart =  vaddr + (brd->bd_uart_offset * i);
                else
                        ch->ch_cls_uart =  vaddr + (brd->bd_uart_offset * i);

                ch->ch_bd = brd;
                ch->ch_portnum = i;

                /* .25 second delay */
                ch->ch_close_delay = 250;

                init_waitqueue_head(&ch->ch_flags_wait);
        }

        jsm_dbg(INIT, &brd->pci_dev, "finish\n");
        return 0;
}

int jsm_uart_port_init(struct jsm_board *brd)
{
        int i, rc;
        unsigned int line;

        if (!brd)
                return -ENXIO;

        jsm_dbg(INIT, &brd->pci_dev, "start\n");

        /*
         * Initialize board structure elements.
         */

        brd->nasync = brd->maxports;

        /* Set up channel variables */
        for (i = 0; i < brd->nasync; i++) {

                if (!brd->channels[i])
                        continue;

                brd->channels[i]->uart_port.dev = &brd->pci_dev->dev;
                brd->channels[i]->uart_port.irq = brd->irq;
                brd->channels[i]->uart_port.uartclk = 14745600;
                brd->channels[i]->uart_port.type = PORT_JSM;
                brd->channels[i]->uart_port.iotype = UPIO_MEM;
                brd->channels[i]->uart_port.membase = brd->re_map_membase;
                brd->channels[i]->uart_port.fifosize = 16;
                brd->channels[i]->uart_port.ops = &jsm_ops;
                line = find_first_zero_bit(linemap, MAXLINES);
                if (line >= MAXLINES) {
                        printk(KERN_INFO "jsm: linemap is full, added device failed\n");
                        continue;
                } else
                        set_bit(line, linemap);
                brd->channels[i]->uart_port.line = line;
                rc = uart_add_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port);
                if (rc) {
                        printk(KERN_INFO "jsm: Port %d failed. Aborting...\n", i);
                        return rc;
                } else
                        printk(KERN_INFO "jsm: Port %d added\n", i);
        }

        jsm_dbg(INIT, &brd->pci_dev, "finish\n");
        return 0;
}

int jsm_remove_uart_port(struct jsm_board *brd)
{
        int i;
        struct jsm_channel *ch;

        if (!brd)
                return -ENXIO;

        jsm_dbg(INIT, &brd->pci_dev, "start\n");

        /*
         * Initialize board structure elements.
         */

        brd->nasync = brd->maxports;

        /* Set up channel variables */
        for (i = 0; i < brd->nasync; i++) {

                if (!brd->channels[i])
                        continue;

                ch = brd->channels[i];

                clear_bit(ch->uart_port.line, linemap);
                uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port);
        }

        jsm_dbg(INIT, &brd->pci_dev, "finish\n");
        return 0;
}

void jsm_input(struct jsm_channel *ch)
{
        struct jsm_board *bd;
        struct tty_struct *tp;
        struct tty_port *port;
        u32 rmask;
        u16 head;
        u16 tail;
        int data_len;
        unsigned long lock_flags;
        int len = 0;
        int s = 0;
        int i = 0;

        jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n");

        port = &ch->uart_port.state->port;
        tp = port->tty;

        bd = ch->ch_bd;
        if (!bd)
                return;

        spin_lock_irqsave(&ch->ch_lock, lock_flags);

        /*
         *Figure the number of characters in the buffer.
         *Exit immediately if none.
         */

        rmask = RQUEUEMASK;

        head = ch->ch_r_head & rmask;
        tail = ch->ch_r_tail & rmask;

        data_len = (head - tail) & rmask;
        if (data_len == 0) {
                spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
                return;
        }

        jsm_dbg(READ, &ch->ch_bd->pci_dev, "start\n");

        /*
         *If the device is not open, or CREAD is off, flush
         *input data and return immediately.
         */
        if (!tp || !C_CREAD(tp)) {

                jsm_dbg(READ, &ch->ch_bd->pci_dev,
                        "input. dropping %d bytes on port %d...\n",
                        data_len, ch->ch_portnum);
                ch->ch_r_head = tail;

                /* Force queue flow control to be released, if needed */
                jsm_check_queue_flow_control(ch);

                spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
                return;
        }

        /*
         * If we are throttled, simply don't read any data.
         */
        if (ch->ch_flags & CH_STOPI) {
                spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
                jsm_dbg(READ, &ch->ch_bd->pci_dev,
                        "Port %d throttled, not reading any data. head: %x tail: %x\n",
                        ch->ch_portnum, head, tail);
                return;
        }

        jsm_dbg(READ, &ch->ch_bd->pci_dev, "start 2\n");

        len = tty_buffer_request_room(port, data_len);

        /*
         * len now contains the most amount of data we can copy,
         * bounded either by the flip buffer size or the amount
         * of data the card actually has pending...
         */
        while (len) {
                s = ((head >= tail) ? head : RQUEUESIZE) - tail;
                s = min(s, len);

                if (s <= 0)
                        break;

                        /*
                         * If conditions are such that ld needs to see all
                         * UART errors, we will have to walk each character
                         * and error byte and send them to the buffer one at
                         * a time.
                         */

                if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {
                        for (i = 0; i < s; i++) {
                                u8 chr   = ch->ch_rqueue[tail + i];
                                u8 error = ch->ch_equeue[tail + i];
                                char flag = TTY_NORMAL;

                                /*
                                 * Give the Linux ld the flags in the format it
                                 * likes.
                                 */
                                if (error & UART_LSR_BI)
                                        flag = TTY_BREAK;
                                else if (error & UART_LSR_PE)
                                        flag = TTY_PARITY;
                                else if (error & UART_LSR_FE)
                                        flag = TTY_FRAME;

                                tty_insert_flip_char(port, chr, flag);
                        }
                } else {
                        tty_insert_flip_string(port, ch->ch_rqueue + tail, s);
                }
                tail += s;
                len -= s;
                /* Flip queue if needed */
                tail &= rmask;
        }

        ch->ch_r_tail = tail & rmask;
        ch->ch_e_tail = tail & rmask;
        jsm_check_queue_flow_control(ch);
        spin_unlock_irqrestore(&ch->ch_lock, lock_flags);

        /* Tell the tty layer its okay to "eat" the data now */
        tty_flip_buffer_push(port);

        jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n");
}

static void jsm_carrier(struct jsm_channel *ch)
{
        struct jsm_board *bd;

        int virt_carrier = 0;
        int phys_carrier = 0;

        jsm_dbg(CARR, &ch->ch_bd->pci_dev, "start\n");

        bd = ch->ch_bd;
        if (!bd)
                return;

        if (ch->ch_mistat & UART_MSR_DCD) {
                jsm_dbg(CARR, &ch->ch_bd->pci_dev, "mistat: %x D_CD: %x\n",
                        ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD);
                phys_carrier = 1;
        }

        if (ch->ch_c_cflag & CLOCAL)
                virt_carrier = 1;

        jsm_dbg(CARR, &ch->ch_bd->pci_dev, "DCD: physical: %d virt: %d\n",
                phys_carrier, virt_carrier);

        /*
         * Test for a VIRTUAL carrier transition to HIGH.
         */
        if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) {

                /*
                 * When carrier rises, wake any threads waiting
                 * for carrier in the open routine.
                 */

                jsm_dbg(CARR, &ch->ch_bd->pci_dev, "carrier: virt DCD rose\n");

                if (waitqueue_active(&(ch->ch_flags_wait)))
                        wake_up_interruptible(&ch->ch_flags_wait);
        }

        /*
         * Test for a PHYSICAL carrier transition to HIGH.
         */
        if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) {

                /*
                 * When carrier rises, wake any threads waiting
                 * for carrier in the open routine.
                 */

                jsm_dbg(CARR, &ch->ch_bd->pci_dev,
                        "carrier: physical DCD rose\n");

                if (waitqueue_active(&(ch->ch_flags_wait)))
                        wake_up_interruptible(&ch->ch_flags_wait);
        }

        /*
         *  Test for a PHYSICAL transition to low, so long as we aren't
         *  currently ignoring physical transitions (which is what "virtual
         *  carrier" indicates).
         *
         *  The transition of the virtual carrier to low really doesn't
         *  matter... it really only means "ignore carrier state", not
         *  "make pretend that carrier is there".
         */
        if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0)
                        && (phys_carrier == 0)) {
                /*
                 *      When carrier drops:
                 *
                 *      Drop carrier on all open units.
                 *
                 *      Flush queues, waking up any task waiting in the
                 *      line discipline.
                 *
                 *      Send a hangup to the control terminal.
                 *
                 *      Enable all select calls.
                 */
                if (waitqueue_active(&(ch->ch_flags_wait)))
                        wake_up_interruptible(&ch->ch_flags_wait);
        }

        /*
         *  Make sure that our cached values reflect the current reality.
         */
        if (virt_carrier == 1)
                ch->ch_flags |= CH_FCAR;
        else
                ch->ch_flags &= ~CH_FCAR;

        if (phys_carrier == 1)
                ch->ch_flags |= CH_CD;
        else
                ch->ch_flags &= ~CH_CD;
}


void jsm_check_queue_flow_control(struct jsm_channel *ch)
{
        struct board_ops *bd_ops = ch->ch_bd->bd_ops;
        int qleft;

        /* Store how much space we have left in the queue */
        qleft = ch->ch_r_tail - ch->ch_r_head - 1;
        if (qleft < 0)
                qleft += RQUEUEMASK + 1;

        /*
         * Check to see if we should enforce flow control on our queue because
         * the ld (or user) isn't reading data out of our queue fast enuf.
         *
         * NOTE: This is done based on what the current flow control of the
         * port is set for.
         *
         * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt.
         *      This will cause the UART's FIFO to back up, and force
         *      the RTS signal to be dropped.
         * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to
         *      the other side, in hopes it will stop sending data to us.
         * 3) NONE - Nothing we can do.  We will simply drop any extra data
         *      that gets sent into us when the queue fills up.
         */
        if (qleft < 256) {
                /* HWFLOW */
                if (ch->ch_c_cflag & CRTSCTS) {
                        if (!(ch->ch_flags & CH_RECEIVER_OFF)) {
                                bd_ops->disable_receiver(ch);
                                ch->ch_flags |= (CH_RECEIVER_OFF);
                                jsm_dbg(READ, &ch->ch_bd->pci_dev,
                                        "Internal queue hit hilevel mark (%d)! Turning off interrupts\n",
                                        qleft);
                        }
                }
                /* SWFLOW */
                else if (ch->ch_c_iflag & IXOFF) {
                        if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
                                bd_ops->send_stop_character(ch);
                                ch->ch_stops_sent++;
                                jsm_dbg(READ, &ch->ch_bd->pci_dev,
                                        "Sending stop char! Times sent: %x\n",
                                        ch->ch_stops_sent);
                        }
                }
        }

        /*
         * Check to see if we should unenforce flow control because
         * ld (or user) finally read enuf data out of our queue.
         *
         * NOTE: This is done based on what the current flow control of the
         * port is set for.
         *
         * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt.
         *      This will cause the UART's FIFO to raise RTS back up,
         *      which will allow the other side to start sending data again.
         * 2) SWFLOW (IXOFF) - Send a start character to
         *      the other side, so it will start sending data to us again.
         * 3) NONE - Do nothing. Since we didn't do anything to turn off the
         *      other side, we don't need to do anything now.
         */
        if (qleft > (RQUEUESIZE / 2)) {
                /* HWFLOW */
                if (ch->ch_c_cflag & CRTSCTS) {
                        if (ch->ch_flags & CH_RECEIVER_OFF) {
                                bd_ops->enable_receiver(ch);
                                ch->ch_flags &= ~(CH_RECEIVER_OFF);
                                jsm_dbg(READ, &ch->ch_bd->pci_dev,
                                        "Internal queue hit lowlevel mark (%d)! Turning on interrupts\n",
                                        qleft);
                        }
                }
                /* SWFLOW */
                else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
                        ch->ch_stops_sent = 0;
                        bd_ops->send_start_character(ch);
                        jsm_dbg(READ, &ch->ch_bd->pci_dev,
                                "Sending start char!\n");
                }
        }
}