diff options
Diffstat (limited to 'drivers/staging/dgrp/dgrp_tty.c')
-rw-r--r-- | drivers/staging/dgrp/dgrp_tty.c | 3337 |
1 files changed, 0 insertions, 3337 deletions
diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c deleted file mode 100644 index 30d26029b21e..000000000000 --- a/drivers/staging/dgrp/dgrp_tty.c +++ /dev/null @@ -1,3337 +0,0 @@ -/* - * - * Copyright 1999 Digi International (www.digi.com) - * Gene Olson <Gene_Olson at digi dot com> - * James Puzzo <jamesp at digi dot com> - * Jeff Randall - * Scott Kilau <scottk at digi dot com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the - * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - */ - -/* - * - * Filename: - * - * dgrp_tty.c - * - * Description: - * - * This file implements the tty driver functionality for the - * RealPort driver software. - * - * Author: - * - * James A. Puzzo - * Ann-Marie Westgate - * - */ - -#include <linux/slab.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/device.h> -#include <linux/sched.h> -#include <linux/uaccess.h> - -#include "dgrp_common.h" - -#ifndef _POSIX_VDISABLE -#define _POSIX_VDISABLE ('\0') -#endif - -/* - * forward declarations - */ - -static void drp_param(struct ch_struct *); -static void dgrp_tty_close(struct tty_struct *, struct file *); - -/* ioctl helper functions */ -static int set_modem_info(struct ch_struct *, unsigned int, unsigned int *); -static int get_modem_info(struct ch_struct *, unsigned int *); -static void dgrp_set_custom_speed(struct ch_struct *, int); -static int dgrp_tty_digigetedelay(struct tty_struct *, int *); -static int dgrp_tty_digisetedelay(struct tty_struct *, int *); -static int dgrp_send_break(struct ch_struct *, int); - -static ushort tty_to_ch_flags(struct tty_struct *, char); -static tcflag_t ch_to_tty_flags(unsigned short, char); - -static void dgrp_tty_input_start(struct tty_struct *); -static void dgrp_tty_input_stop(struct tty_struct *); - -static void drp_wmove(struct ch_struct *, int, void*, int); - -static int dgrp_tty_open(struct tty_struct *, struct file *); -static void dgrp_tty_close(struct tty_struct *, struct file *); -static int dgrp_tty_write(struct tty_struct *, const unsigned char *, int); -static int dgrp_tty_write_room(struct tty_struct *); -static void dgrp_tty_flush_buffer(struct tty_struct *); -static int dgrp_tty_chars_in_buffer(struct tty_struct *); -static int dgrp_tty_ioctl(struct tty_struct *, unsigned int, unsigned long); -static void dgrp_tty_set_termios(struct tty_struct *, struct ktermios *); -static void dgrp_tty_stop(struct tty_struct *); -static void dgrp_tty_start(struct tty_struct *); -static void dgrp_tty_throttle(struct tty_struct *); -static void dgrp_tty_unthrottle(struct tty_struct *); -static void dgrp_tty_hangup(struct tty_struct *); -static int dgrp_tty_put_char(struct tty_struct *, unsigned char); -static int dgrp_tty_tiocmget(struct tty_struct *); -static int dgrp_tty_tiocmset(struct tty_struct *, unsigned int, unsigned int); -static int dgrp_tty_send_break(struct tty_struct *, int); -static void dgrp_tty_send_xchar(struct tty_struct *, char); - -/* - * tty defines - */ -#define SERIAL_TYPE_NORMAL 1 -#define SERIAL_TYPE_CALLOUT 2 -#define SERIAL_TYPE_XPRINT 3 - - -/* - * tty globals/statics - */ - - -#define PORTSERVER_DIVIDEND 1843200 - -/* - * Default transparent print information. - */ -static struct digi_struct digi_init = { - .digi_flags = DIGI_COOK, /* Flags */ - .digi_maxcps = 100, /* Max CPS */ - .digi_maxchar = 50, /* Max chars in print queue */ - .digi_bufsize = 100, /* Printer buffer size */ - .digi_onlen = 4, /* size of printer on string */ - .digi_offlen = 4, /* size of printer off string */ - .digi_onstr = "\033[5i", /* ANSI printer on string */ - .digi_offstr = "\033[4i", /* ANSI printer off string */ - .digi_term = "ansi" /* default terminal type */ -}; - -/* - * Define a local default termios struct. All ports will be created - * with this termios initially. - * - * This defines a raw port at 9600 baud, 8 data bits, no parity, - * 1 stop bit. - */ -static struct ktermios DefaultTermios = { - .c_iflag = (ICRNL | IXON), - .c_oflag = (OPOST | ONLCR), - .c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL), - .c_lflag = (ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL - | ECHOKE | IEXTEN), - .c_cc = INIT_C_CC, - .c_line = 0, -}; - -/* Define our tty operations struct */ -static const struct tty_operations dgrp_tty_ops = { - .open = dgrp_tty_open, - .close = dgrp_tty_close, - .write = dgrp_tty_write, - .write_room = dgrp_tty_write_room, - .flush_buffer = dgrp_tty_flush_buffer, - .chars_in_buffer = dgrp_tty_chars_in_buffer, - .flush_chars = NULL, - .ioctl = dgrp_tty_ioctl, - .set_termios = dgrp_tty_set_termios, - .stop = dgrp_tty_stop, - .start = dgrp_tty_start, - .throttle = dgrp_tty_throttle, - .unthrottle = dgrp_tty_unthrottle, - .hangup = dgrp_tty_hangup, - .put_char = dgrp_tty_put_char, - .tiocmget = dgrp_tty_tiocmget, - .tiocmset = dgrp_tty_tiocmset, - .break_ctl = dgrp_tty_send_break, - .send_xchar = dgrp_tty_send_xchar -}; - - -static int calc_baud_rate(struct un_struct *un) -{ - int i; - int brate; - - struct baud_rates { - unsigned int rate; - unsigned int cflag; - }; - - static struct baud_rates baud_rates[] = { - { 921600, B921600 }, - { 460800, B460800 }, - { 230400, B230400 }, - { 115200, B115200 }, - { 57600, B57600 }, - { 38400, B38400 }, - { 19200, B19200 }, - { 9600, B9600 }, - { 4800, B4800 }, - { 2400, B2400 }, - { 1200, B1200 }, - { 600, B600 }, - { 300, B300 }, - { 200, B200 }, - { 150, B150 }, - { 134, B134 }, - { 110, B110 }, - { 75, B75 }, - { 50, B50 }, - { 0, B9600 } - }; - - brate = C_BAUD(un->un_tty); - - for (i = 0; baud_rates[i].rate; i++) { - if (baud_rates[i].cflag == brate) - break; - } - - return baud_rates[i].rate; -} - -static int calc_fastbaud_rate(struct un_struct *un, struct ktermios *uts) -{ - int i; - int brate; - - ulong bauds[2][16] = { - { /* fastbaud*/ - 0, 57600, 76800, 115200, - 131657, 153600, 230400, 460800, - 921600, 1200, 1800, 2400, - 4800, 9600, 19200, 38400 }, - { /* fastbaud & CBAUDEX */ - 0, 57600, 115200, 230400, - 460800, 150, 200, 921600, - 600, 1200, 1800, 2400, - 4800, 9600, 19200, 38400 } - }; - - brate = C_BAUD(un->un_tty) & 0xff; - - i = (uts->c_cflag & CBAUDEX) ? 1 : 0; - - - if ((i >= 0) && (i < 2) && (brate >= 0) && (brate < 16)) - brate = bauds[i][brate]; - else - brate = 0; - - return brate; -} - -/** - * drp_param() -- send parameter values to be sent to the node - * @ch: channel structure of port to modify - * - * Interprets the tty and modem changes made by an application - * program (by examining the termios structures) and sets up - * parameter values to be sent to the node. - */ -static void drp_param(struct ch_struct *ch) -{ - struct nd_struct *nd; - struct un_struct *un; - int brate; - int mflow; - int xflag; - int iflag; - struct ktermios *tts, *pts, *uts; - - nd = ch->ch_nd; - - /* - * If the terminal device is open, use it to set up all tty - * modes and functions. Otherwise use the printer device. - */ - - if (ch->ch_tun.un_open_count) { - - un = &ch->ch_tun; - tts = &ch->ch_tun.un_tty->termios; - - /* - * If both devices are open, copy critical line - * parameters from the tty device to the printer, - * so that if the tty is closed, the printer will - * continue without disruption. - */ - - if (ch->ch_pun.un_open_count) { - - pts = &ch->ch_pun.un_tty->termios; - - pts->c_cflag ^= - (pts->c_cflag ^ tts->c_cflag) & - (CBAUD | CSIZE | CSTOPB | CREAD | PARENB | - PARODD | HUPCL | CLOCAL); - - pts->c_iflag ^= - (pts->c_iflag ^ tts->c_iflag) & - (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | - ISTRIP | IXON | IXANY | IXOFF); - - pts->c_cc[VSTART] = tts->c_cc[VSTART]; - pts->c_cc[VSTOP] = tts->c_cc[VSTOP]; - } - } else if (ch->ch_pun.un_open_count == 0) { - pr_warn("%s - ch_pun.un_open_count shouldn't be 0\n", - __func__); - return; - } else { - un = &ch->ch_pun; - } - - uts = &un->un_tty->termios; - - /* - * Determine if FAST writes can be performed. - */ - - if ((ch->ch_digi.digi_flags & DIGI_COOK) != 0 && - (ch->ch_tun.un_open_count != 0) && - !((un->un_tty)->ldisc->ops->flags & LDISC_FLAG_DEFINED) && - !(L_XCASE(un->un_tty))) { - ch->ch_flag |= CH_FAST_WRITE; - } else { - ch->ch_flag &= ~CH_FAST_WRITE; - } - - /* - * If FAST writes can be performed, and OPOST is on in the - * terminal device, do OPOST handling in the server. - */ - - if ((ch->ch_flag & CH_FAST_WRITE) && - O_OPOST(un->un_tty) != 0) { - int oflag = tty_to_ch_flags(un->un_tty, 'o'); - - /* add to ch_ocook any processing flags set in the termio */ - ch->ch_ocook |= oflag & (OF_OLCUC | - OF_ONLCR | - OF_OCRNL | - OF_ONLRET | - OF_TABDLY); - - /* - * the hpux driver clears any flags set in ch_ocook - * from the termios oflag. It is STILL reported though - * by a TCGETA - */ - - oflag = ch_to_tty_flags(ch->ch_ocook, 'o'); - uts->c_oflag &= ~oflag; - - } else { - /* clear the ch->ch_ocook flag */ - int oflag = ch_to_tty_flags(ch->ch_ocook, 'o'); - uts->c_oflag |= oflag; - ch->ch_ocook = 0; - } - - ch->ch_oflag = ch->ch_ocook; - - - ch->ch_flag &= ~CH_FAST_READ; - - /* - * Generate channel flags - */ - - if (C_BAUD(un->un_tty) == B0) { - if (!(ch->ch_flag & CH_BAUD0)) { - /* TODO : the HPUX driver flushes line */ - /* TODO : discipline, I assume I don't have to */ - - ch->ch_tout = ch->ch_tin; - ch->ch_rout = ch->ch_rin; - - ch->ch_break_time = 0; - - ch->ch_send |= RR_TX_FLUSH | RR_RX_FLUSH; - - ch->ch_mout &= ~(DM_DTR | DM_RTS); - - ch->ch_flag |= CH_BAUD0; - } - } else if (ch->ch_custom_speed) { - ch->ch_brate = PORTSERVER_DIVIDEND / ch->ch_custom_speed; - - if (ch->ch_flag & CH_BAUD0) { - ch->ch_mout |= DM_DTR | DM_RTS; - - ch->ch_flag &= ~CH_BAUD0; - } - } else { - /* - * Baud rate mapping. - * - * If FASTBAUD isn't on, we can scan the new baud rate list - * as required. - * - * However, if FASTBAUD is on, we must go to the old - * baud rate mapping that existed many many moons ago, - * for compatibility reasons. - */ - - if (!(ch->ch_digi.digi_flags & DIGI_FAST)) - brate = calc_baud_rate(un); - else - brate = calc_fastbaud_rate(un, uts); - - if (brate == 0) - brate = 9600; - - ch->ch_brate = PORTSERVER_DIVIDEND / brate; - - if (ch->ch_flag & CH_BAUD0) { - ch->ch_mout |= DM_DTR | DM_RTS; - - ch->ch_flag &= ~CH_BAUD0; - } - } - - /* - * Generate channel cflags from the termio. - */ - - ch->ch_cflag = tty_to_ch_flags(un->un_tty, 'c'); - - /* - * Generate channel iflags from the termio. - */ - - iflag = (int) tty_to_ch_flags(un->un_tty, 'i'); - - if (START_CHAR(un->un_tty) == _POSIX_VDISABLE || - STOP_CHAR(un->un_tty) == _POSIX_VDISABLE) { - iflag &= ~(IF_IXON | IF_IXANY | IF_IXOFF); - } - - ch->ch_iflag = iflag; - - /* - * Generate flow control characters - */ - - /* - * From the POSIX.1 spec (7.1.2.6): "If {_POSIX_VDISABLE} - * is defined for the terminal device file, and the value - * of one of the changeable special control characters (see - * 7.1.1.9) is {_POSIX_VDISABLE}, that function shall be - * disabled, that is, no input data shall be recognized as - * the disabled special character." - * - * OK, so we don't ever assign S/DXB XON or XOFF to _POSIX_VDISABLE. - */ - - if (uts->c_cc[VSTART] != _POSIX_VDISABLE) - ch->ch_xon = uts->c_cc[VSTART]; - if (uts->c_cc[VSTOP] != _POSIX_VDISABLE) - ch->ch_xoff = uts->c_cc[VSTOP]; - - ch->ch_lnext = (uts->c_cc[VLNEXT] == _POSIX_VDISABLE ? 0 : - uts->c_cc[VLNEXT]); - - /* - * Also, if either c_cc[START] or c_cc[STOP] is set to - * _POSIX_VDISABLE, we can't really do software flow - * control--in either direction--so we turn it off as - * far as S/DXB is concerned. In essence, if you disable - * one, you disable the other too. - */ - if ((uts->c_cc[VSTART] == _POSIX_VDISABLE) || - (uts->c_cc[VSTOP] == _POSIX_VDISABLE)) - ch->ch_iflag &= ~(IF_IXOFF | IF_IXON); - - /* - * Update xflags. - */ - - xflag = 0; - - if (ch->ch_digi.digi_flags & DIGI_AIXON) - xflag = XF_XIXON; - - if ((ch->ch_xxon == _POSIX_VDISABLE) || - (ch->ch_xxoff == _POSIX_VDISABLE)) - xflag &= ~XF_XIXON; - - ch->ch_xflag = xflag; - - - /* - * Figure effective DCD value. - */ - - if (C_CLOCAL(un->un_tty)) - ch->ch_flag |= CH_CLOCAL; - else - ch->ch_flag &= ~CH_CLOCAL; - - /* - * Check modem signals - */ - - dgrp_carrier(ch); - - /* - * Get hardware handshake value. - */ - - mflow = 0; - - if (C_CRTSCTS(un->un_tty)) - mflow |= (DM_RTS | DM_CTS); - - if (ch->ch_digi.digi_flags & RTSPACE) - mflow |= DM_RTS; - - if (ch->ch_digi.digi_flags & DTRPACE) - mflow |= DM_DTR; - - if (ch->ch_digi.digi_flags & CTSPACE) - mflow |= DM_CTS; - - if (ch->ch_digi.digi_flags & DSRPACE) - mflow |= DM_DSR; - - if (ch->ch_digi.digi_flags & DCDPACE) - mflow |= DM_CD; - - if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) - mflow |= DM_RTS_TOGGLE; - - ch->ch_mflow = mflow; - - /* - * Send the changes to the server. - */ - - ch->ch_flag |= CH_PARAM; - (ch->ch_nd)->nd_tx_work = 1; - - if (waitqueue_active(&ch->ch_flag_wait)) - wake_up_interruptible(&ch->ch_flag_wait); -} - -/* - * This function is just used as a callback for timeouts - * waiting on the ch_sleep flag. - */ -static void wake_up_drp_sleep_timer(unsigned long ptr) -{ - struct ch_struct *ch = (struct ch_struct *) ptr; - if (ch) - wake_up(&ch->ch_sleep); -} - - -/* - * Set up our own sleep that can't be cancelled - * until our timeout occurs. - */ -static void drp_my_sleep(struct ch_struct *ch) -{ - struct timer_list drp_wakeup_timer; - DECLARE_WAITQUEUE(wait, current); - - /* - * First make sure we're ready to receive the wakeup. - */ - - add_wait_queue(&ch->ch_sleep, &wait); - current->state = TASK_UNINTERRUPTIBLE; - - /* - * Since we are uninterruptible, set a timer to - * unset the uninterruptable state in 1 second. - */ - - init_timer(&drp_wakeup_timer); - drp_wakeup_timer.function = wake_up_drp_sleep_timer; - drp_wakeup_timer.data = (unsigned long) ch; - drp_wakeup_timer.expires = jiffies + (1 * HZ); - add_timer(&drp_wakeup_timer); - - schedule(); - - del_timer(&drp_wakeup_timer); - - remove_wait_queue(&ch->ch_sleep, &wait); -} - -/* - * dgrp_tty_open() - * - * returns: - * -EBUSY - this is a callout device and the normal device is active - * - there is an error in opening the tty - * -ENODEV - the channel does not exist - * -EAGAIN - we are in the middle of hanging up or closing - * - IMMEDIATE_OPEN fails - * -ENXIO or -EAGAIN - * - if the port is outside physical range - * -EINTR - the open is interrupted - * - */ -static int dgrp_tty_open(struct tty_struct *tty, struct file *file) -{ - int retval = 0; - struct nd_struct *nd; - struct ch_struct *ch; - struct un_struct *un; - int port; - int delay_error; - int otype; - int unf; - int wait_carrier; - int category; - int counts_were_incremented = 0; - ulong lock_flags; - DECLARE_WAITQUEUE(wait, current); - - /* - * Do some initial checks to see if the node and port exist - */ - - nd = nd_struct_get(MAJOR(tty_devnum(tty))); - port = PORT_NUM(MINOR(tty_devnum(tty))); - category = OPEN_CATEGORY(MINOR(tty_devnum(tty))); - - if (!nd) - return -ENODEV; - - if (port >= CHAN_MAX) - return -ENODEV; - - /* - * The channel exists. - */ - - ch = nd->nd_chan + port; - - un = IS_PRINT(MINOR(tty_devnum(tty))) ? &ch->ch_pun : &ch->ch_tun; - un->un_tty = tty; - tty->driver_data = un; - - /* - * If we are in the middle of hanging up, - * then return an error - */ - if (tty_hung_up_p(file)) { - retval = ((un->un_flag & UN_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - goto done; - } - - /* - * If the port is in the middle of closing, then block - * until it is done, then try again. - */ - retval = wait_event_interruptible(un->un_close_wait, - ((un->un_flag & UN_CLOSING) == 0)); - - if (retval) - goto done; - - /* - * If the port is in the middle of a reopen after a network disconnect, - * wait until it is done, then try again. - */ - retval = wait_event_interruptible(ch->ch_flag_wait, - ((ch->ch_flag & CH_PORT_GONE) == 0)); - - if (retval) - goto done; - - /* - * If this is a callout device, then just make sure the normal - * device isn't being used. - */ - - if (tty->driver->subtype == SERIAL_TYPE_CALLOUT) { - if (un->un_flag & UN_NORMAL_ACTIVE) { - retval = -EBUSY; - goto done; - } else { - un->un_flag |= UN_CALLOUT_ACTIVE; - } - } - - /* - * Loop waiting until the open can be successfully completed. - */ - - spin_lock_irqsave(&nd->nd_lock, lock_flags); - - nd->nd_tx_work = 1; - - for (;;) { - wait_carrier = 0; - - /* - * Determine the open type from the flags provided. - */ - - /* - * If the port is not enabled, then exit - */ - if (test_bit(TTY_IO_ERROR, &tty->flags)) { - /* there was an error in opening the tty */ - if (un->un_flag & UN_CALLOUT_ACTIVE) - retval = -EBUSY; - else - un->un_flag |= UN_NORMAL_ACTIVE; - goto unlock; - } - - if (file->f_flags & O_NONBLOCK) { - - /* - * if the O_NONBLOCK is set, errors on read and write - * must return -EAGAIN immediately and NOT sleep - * on the waitqs. - */ - otype = OTYPE_IMMEDIATE; - delay_error = -EAGAIN; - - } else if (!OPEN_WAIT_AVAIL(category) || - (file->f_flags & O_NDELAY) != 0) { - otype = OTYPE_IMMEDIATE; - delay_error = -EBUSY; - - } else if (!OPEN_WAIT_CARRIER(category) || - ((ch->ch_digi.digi_flags & DIGI_FORCEDCD) != 0) || - C_CLOCAL(tty)) { - otype = OTYPE_PERSISTENT; - delay_error = 0; - - } else { - otype = OTYPE_INCOMING; - delay_error = 0; - } - - /* - * Handle port currently outside physical port range. - */ - - if (port >= nd->nd_chan_count) { - if (otype == OTYPE_IMMEDIATE) { - retval = (nd->nd_state == NS_READY) ? - -ENXIO : -EAGAIN; - goto unlock; - } - } - - /* - * Handle port not currently open. - */ - - else if (ch->ch_open_count == 0) { - /* - * Return an error when an Incoming Open - * response indicates the port is busy. - */ - - if (ch->ch_open_error != 0 && otype == ch->ch_otype) { - retval = (ch->ch_open_error <= 2) ? - delay_error : -ENXIO; - goto unlock; - } - - /* - * Fail any new Immediate open if we do not have - * a normal connection to the server. - */ - - if (nd->nd_state != NS_READY && - otype == OTYPE_IMMEDIATE) { - retval = -EAGAIN; - goto unlock; - } - - /* - * If a Realport open of the correct type has - * succeeded, complete the open. - */ - - if (ch->ch_state == CS_READY && ch->ch_otype == otype) - break; - } - - /* - * Handle port already open and active as a device - * of same category. - */ - - else if ((ch->ch_category == category) || - IS_PRINT(MINOR(tty_devnum(tty)))) { - /* - * Fail if opening the device now would - * violate exclusive use. - */ - unf = ch->ch_tun.un_flag | ch->ch_pun.un_flag; - - if ((file->f_flags & O_EXCL) || (unf & UN_EXCL)) { - retval = -EBUSY; - goto unlock; - } - - /* - * If the open device is in the hangup state, all - * system calls fail except close(). - */ - - /* TODO : check on hangup_p calls */ - - if (ch->ch_flag & CH_HANGUP) { - retval = -ENXIO; - goto unlock; - } - - /* - * If the port is ready, and carrier is ignored - * or present, then complete the open. - */ - - if (ch->ch_state == CS_READY && - (otype != OTYPE_INCOMING || - ch->ch_flag & CH_VIRT_CD)) - break; - - wait_carrier = 1; - } - - /* - * Handle port active with a different category device. - */ - - else { - if (otype == OTYPE_IMMEDIATE) { - retval = delay_error; - goto unlock; - } - } - - /* - * Wait until conditions change, then take another - * try at the open. - */ - - ch->ch_wait_count[otype]++; - - if (wait_carrier) - ch->ch_wait_carrier++; - - /* - * Prepare the task to accept the wakeup, then - * release our locks and release control. - */ - - add_wait_queue(&ch->ch_flag_wait, &wait); - current->state = TASK_INTERRUPTIBLE; - - spin_unlock_irqrestore(&nd->nd_lock, lock_flags); - - /* - * Give up control, we'll come back if we're - * interrupted or are woken up. - */ - schedule(); - remove_wait_queue(&ch->ch_flag_wait, &wait); - - spin_lock_irqsave(&nd->nd_lock, lock_flags); - - current->state = TASK_RUNNING; - - ch->ch_wait_count[otype]--; - - if (wait_carrier) - ch->ch_wait_carrier--; - - nd->nd_tx_work = 1; - - if (signal_pending(current)) { - retval = -EINTR; - goto unlock; - } - } /* end for(;;) */ - - /* - * The open has succeeded. No turning back. - */ - counts_were_incremented = 1; - un->un_open_count++; - ch->ch_open_count++; - - /* - * Initialize the channel, if it's not already open. - */ - - if (ch->ch_open_count == 1) { - ch->ch_flag = 0; - ch->ch_inwait = 0; - ch->ch_category = category; - ch->ch_pscan_state = 0; - - /* TODO : find out what PS-1 bug Gene was referring to */ - /* TODO : in the following comment. */ - - ch->ch_send = RR_TX_START | RR_RX_START; /* PS-1 bug */ - - if (C_CLOCAL(tty) || - ch->ch_s_mlast & DM_CD || - ch->ch_digi.digi_flags & DIGI_FORCEDCD) - ch->ch_flag |= CH_VIRT_CD; - else if (OPEN_FORCES_CARRIER(category)) - ch->ch_flag |= CH_VIRT_CD; - - } - - /* - * Initialize the unit, if it is not already open. - */ - - if (un->un_open_count == 1) { - /* - * Since all terminal options are always sticky in Linux, - * we don't need the UN_STICKY flag to be handled specially. - */ - /* clears all the digi flags, leaves serial flags */ - un->un_flag &= ~UN_DIGI_MASK; - - if (file->f_flags & O_EXCL) - un->un_flag |= UN_EXCL; - - /* TODO : include "session" and "pgrp" */ - - /* - * In Linux, all terminal parameters are intended to be sticky. - * as a result, we "remove" the code which once reset the ports - * to sane values. - */ - - drp_param(ch); - - } - - un->un_flag |= UN_INITIALIZED; - - retval = 0; - -unlock: - - spin_unlock_irqrestore(&nd->nd_lock, lock_flags); - -done: - /* - * Linux does a close for every open, even failed ones! - */ - if (!counts_were_incremented) { - un->un_open_count++; - ch->ch_open_count++; - } - - if (retval) - dev_err(tty->dev, "tty open bad return (%i)\n", retval); - - return retval; -} - - - - -/* - * dgrp_tty_close() -- close function for tty_operations - */ -static void dgrp_tty_close(struct tty_struct *tty, struct file *file) -{ - struct ch_struct *ch; - struct un_struct *un; - struct nd_struct *nd; - int tpos; - int port; - int err = 0; - int s = 0; - ulong waketime; - ulong lock_flags; - int sent_printer_offstr = 0; - - port = PORT_NUM(MINOR(tty_devnum(tty))); - - un = tty->driver_data; - - if (!un) - return; - - ch = un->un_ch; - - if (!ch) - return; - - nd = ch->ch_nd; - - if (!nd) - return; - - spin_lock_irqsave(&nd->nd_lock, lock_flags); - - - /* Used to be on channel basis, now we check on a unit basis. */ - if (un->un_open_count != 1) - goto unlock; - - /* - * OK, its the last close on the unit - */ - un->un_flag |= UN_CLOSING; - - /* - * Notify the discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - - /* - * Wait for output to drain only if this is - * the last close against the channel - */ - - if (ch->ch_open_count == 1) { - /* - * If its the print device, we need to ensure at all costs that - * the offstr will fit. If it won't, flush our tbuf. - */ - if (IS_PRINT(MINOR(tty_devnum(tty))) && - (((ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK) < - ch->ch_digi.digi_offlen)) - ch->ch_tin = ch->ch_tout; - - /* - * Turn off the printer. Don't bother checking to see if its - * IS_PRINT... Since this is the last close the flag is going - * to be cleared, so we MUST make sure the offstr gets inserted - * into tbuf. - */ - - if ((ch->ch_flag & CH_PRON) != 0) { - drp_wmove(ch, 0, ch->ch_digi.digi_offstr, - ch->ch_digi.digi_offlen); - ch->ch_flag &= ~CH_PRON; - sent_printer_offstr = 1; - } - } - - /* - * Wait until either the output queue has drained, or we see - * absolutely no progress for 15 seconds. - */ - - tpos = ch->ch_s_tpos; - - waketime = jiffies + 15 * HZ; - - for (;;) { - - /* - * Make sure the port still exists. - */ - - if (port >= nd->nd_chan_count) { - err = 1; - break; - } - - if (signal_pending(current)) { - err = 1; - break; - } - - /* - * If the port is idle (not opened on the server), we have - * no way of draining/flushing/closing the port on that server. - * So break out of loop. - */ - if (ch->ch_state == CS_IDLE) - break; - - nd->nd_tx_work = 1; - - /* - * Exit if the queues for this unit are empty, - * and either the other unit is still open or all - * data has drained. - */ - - if ((un->un_tty)->ops->chars_in_buffer ? - ((un->un_tty)->ops->chars_in_buffer)(un->un_tty) == 0 : 1) { - - /* - * We don't need to wait for a buffer to drain - * if the other unit is open. - */ - - if (ch->ch_open_count != un->un_open_count) - break; - - /* - * The wait is complete when all queues are - * drained, and any break in progress is complete. - */ - - if (ch->ch_tin == ch->ch_tout && - ch->ch_s_tin == ch->ch_s_tpos && - (ch->ch_send & RR_TX_BREAK) == 0) { - break; - } - } - - /* - * Flush TX data and exit the wait if NDELAY is set, - * or this is not a DIGI printer, and the close timeout - * expires. - */ - - if ((file->f_flags & (O_NDELAY | O_NONBLOCK)) || - ((long)(jiffies - waketime) >= 0 && - (ch->ch_digi.digi_flags & DIGI_PRINTER) == 0)) { - - /* - * If we sent the printer off string, we cannot - * flush our internal buffers, or we might lose - * the offstr. - */ - if (!sent_printer_offstr) - dgrp_tty_flush_buffer(tty); - - spin_unlock_irqrestore(&nd->nd_lock, lock_flags); - tty_ldisc_flush(tty); - spin_lock_irqsave(&nd->nd_lock, lock_flags); - break; - } - - /* - * Otherwise take a short nap. - */ - - ch->ch_flag |= CH_DRAIN; - - spin_unlock_irqrestore(&nd->nd_lock, lock_flags); - - schedule_timeout_interruptible(1); - s = signal_pending(current); - - spin_lock_irqsave(&nd->nd_lock, lock_flags); - - if (s) { - /* - * If we had sent the printer off string, we now have - * some problems. - * - * The system won't let us sleep since we got an error - * back from sleep, presumably because the user did - * a ctrl-c... - * But we need to ensure that the offstr gets sent! - * Thus, we have to do something else besides sleeping. - * The plan: - * 1) Make this task uninterruptable. - * 2) Set up a timer to go off in 1 sec. - * 3) Act as tho we just got out of the sleep above. - * - * Thankfully, in the real world, this just - * never happens. - */ - - if (sent_printer_offstr) { - spin_unlock_irqrestore(&nd->nd_lock, - lock_flags); - drp_my_sleep(ch); - spin_lock_irqsave(&nd->nd_lock, lock_flags); - } else { - err = 1; - break; - } - } - - /* - * Restart the wait if any progress is seen. - */ - - if (ch->ch_s_tpos != tpos) { - tpos = ch->ch_s_tpos; - - /* TODO: this gives us timeout problems with nist ?? */ - waketime = jiffies + 15 * HZ; - } - } - - /* - * Close the line discipline - */ - - /* this is done in tty_io.c */ - /* if ((un->un_tty)->ldisc.close) - * ((un->un_tty)->ldisc.close)(un->un_tty); - */ - - /* don't do this here */ - /* un->un_flag = 0; */ - - /* - * Flush the receive buffer on terminal unit close only. - */ - - if (!IS_PRINT(MINOR(tty_devnum(tty)))) - ch->ch_rout = ch->ch_rin; - - - /* - * Don't permit the close to happen until we get any pending - * sync request responses. - * There could be other ports depending upon the response as well. - * - * Also, don't permit the close to happen until any parameter - * changes have been sent out from the state machine as well. - * This is required because of a ditty -a race with -HUPCL - * We MUST make sure all channel parameters have been sent to the - * Portserver before sending a close. - */ - - if ((err != 1) && (ch->ch_state != CS_IDLE)) { - spin_unlock_irqrestore(&nd->nd_lock, lock_flags); - s = wait_event_interruptible(ch->ch_flag_wait, - ((ch->ch_flag & (CH_WAITING_SYNC | CH_PARAM)) == 0)); - spin_lock_irqsave(&nd->nd_lock, lock_flags); - } - - /* - * Cleanup the channel if last unit open. - */ - - if (ch->ch_open_count == 1) { - ch->ch_flag = 0; - ch->ch_category = 0; - ch->ch_send = 0; - ch->ch_expect = 0; - ch->ch_tout = ch->ch_tin; - /* (un->un_tty)->device = 0; */ - - if (ch->ch_state == CS_READY) - ch->ch_state = CS_SEND_CLOSE; - } - - /* - * Send the changes to the server - */ - if (ch->ch_state != CS_IDLE) { - ch->ch_flag |= CH_PARAM; - wake_up_interruptible(&ch->ch_flag_wait); - } - - nd->nd_tx_work = 1; - nd->nd_tx_ready = 1; - -unlock: - tty->closing = 0; - - if (ch->ch_open_count <= 0) - dev_info(tty->dev, - "%s - unexpected value for ch->ch_open_count: %i\n", - __func__, ch->ch_open_count); - else - ch->ch_open_count--; - - if (un->un_open_count <= 0) - dev_info(tty->dev, - "%s - unexpected value for un->un_open_count: %i\n", - __func__, un->un_open_count); - else - un->un_open_count--; - - un->un_flag &= ~(UN_NORMAL_ACTIVE | UN_CALLOUT_ACTIVE | UN_CLOSING); - if (waitqueue_active(&un->un_close_wait)) - wake_up_interruptible(&un->un_close_wait); - - spin_unlock_irqrestore(&nd->nd_lock, lock_flags); - - return; - -} - -static void drp_wmove(struct ch_struct *ch, int from_user, void *buf, int count) -{ - int n; - int ret = 0; - - ch->ch_nd->nd_tx_work = 1; - - n = TBUF_MAX - ch->ch_tin; - - if (count >= n) { - if (from_user) - ret = copy_from_user(ch->ch_tbuf + ch->ch_tin, - (void __user *) buf, n); - else - memcpy(ch->ch_tbuf + ch->ch_tin, buf, n); - - buf = (char *) buf + n; - count -= n; - ch->ch_tin = 0; - } - - if (from_user) - ret = copy_from_user(ch->ch_tbuf + ch->ch_tin, - (void __user *) buf, count); - else - memcpy(ch->ch_tbuf + ch->ch_tin, buf, count); - - ch->ch_tin += count; -} - - -static int dgrp_calculate_txprint_bounds(struct ch_struct *ch, int space, - int *un_flag) -{ - clock_t tt; - clock_t mt; - unsigned short tmax = 0; - - /* - * If the terminal device is busy, reschedule when - * the terminal device becomes idle. - */ - - if (ch->ch_tun.un_open_count != 0 && - ch->ch_tun.un_tty->ops->chars_in_buffer && - ((ch->ch_tun.un_tty->ops->chars_in_buffer) - (ch->ch_tun.un_tty) != 0)) { - *un_flag = UN_PWAIT; - return 0; - } - - /* - * Assure that whenever there is printer data in the output - * buffer, there always remains enough space after it to - * turn the printer off. - */ - space -= ch->ch_digi.digi_offlen; - - if (space <= 0) { - *un_flag = UN_EMPTY; - return 0; - } - - /* - * We measure printer CPS speed by incrementing - * ch_cpstime by (HZ / digi_maxcps) for every - * character we output, restricting output so - * that ch_cpstime never exceeds lbolt. - * - * However if output has not been done for some - * time, lbolt will grow to very much larger than - * ch_cpstime, which would allow essentially - * unlimited amounts of output until ch_cpstime - * finally caught up. To avoid this, we adjust - * cps_time when necessary so the difference - * between lbolt and ch_cpstime never results - * in sending more than digi_bufsize characters. - * - * This nicely models a printer with an internal - * buffer of digi_bufsize characters. - * - * Get the time between lbolt and ch->ch_cpstime; - */ - - tt = jiffies - ch->ch_cpstime; - - /* - * Compute the time required to send digi_bufsize - * characters. - */ - - mt = HZ * ch->ch_digi.digi_bufsize / ch->ch_digi.digi_maxcps; - - /* - * Compute the number of characters that can be sent - * without violating the time constraint. If the - * direct calculation of this number is bigger than - * digi_bufsize, limit the number to digi_bufsize, - * and adjust cpstime to match. - */ - - if ((clock_t)(tt + HZ) > (clock_t)(mt + HZ)) { - tmax = ch->ch_digi.digi_bufsize; - ch->ch_cpstime = jiffies - mt; - } else { - tmax = ch->ch_digi.digi_maxcps * tt / HZ; - } - - /* - * If the time constraint now binds, limit the transmit - * count accordingly, and tentatively arrange to be - * rescheduled based on time. - */ - - if (tmax < space) { - *un_flag = UN_TIME; - space = tmax; - } - - /* - * Compute the total number of characters we can - * output before the total number of characters known - * to be in the output queue exceeds digi_maxchar. - */ - - tmax = (ch->ch_digi.digi_maxchar - - ((ch->ch_tin - ch->ch_tout) & TBUF_MASK) - - ((ch->ch_s_tin - ch->ch_s_tpos) & 0xffff)); - - - /* - * If the digi_maxchar constraint now holds, limit - * the transmit count accordingly, and arrange to - * be rescheduled when the queue becomes empty. - */ - - if (space > tmax) { - *un_flag = UN_EMPTY; - space = tmax; - } - - if (space <= 0) - *un_flag |= UN_EMPTY; - - return space; -} - - -static int dgrp_tty_write(struct tty_struct *tty, - const unsigned char *buf, - int count) -{ - struct nd_struct *nd; - struct un_struct *un; - struct ch_struct *ch; - int space; - int n; - int t; - int sendcount; - int un_flag; - ulong lock_flags; - - if (tty == NULL) - return 0; - - un = tty->driver_data; - if (!un) - return 0; - - ch = un->un_ch; - if (!ch) - return 0; - - nd = ch->ch_nd; - if (!nd) - return 0; - - /* - * Ignore the request if the channel is not ready. - */ - if (ch->ch_state != CS_READY) - return 0; - - spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags); - - /* - * Ignore the request if output is blocked. - */ - if ((un->un_flag & (UN_EMPTY | UN_LOW | UN_TIME | UN_PWAIT)) != 0) { - count = 0; - goto out; - } - - /* - * Also ignore the request if DPA has this port open, - * and is flow controlled on reading more data. - */ - if (nd->nd_dpa_debug && nd->nd_dpa_flag & DPA_WAIT_SPACE && - nd->nd_dpa_port == MINOR(tty_devnum(ch->ch_tun.un_tty))) { - count = 0; - goto out; - } - - /* - * Limit amount we will write to the amount of space - * available in the channel buffer. - */ - sendcount = 0; - - space = (ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK; - - /* - * Handle the printer device. - */ - - un_flag = UN_LOW; - - if (IS_PRINT(MINOR(tty_devnum(tty)))) { - clock_t tt; - clock_t mt; - unsigned short tmax = 0; - - /* - * If the terminal device is busy, reschedule when - * the terminal device becomes idle. - */ - - if (ch->ch_tun.un_open_count != 0 && - ((ch->ch_tun.un_tty->ops->chars_in_buffer) - (ch->ch_tun.un_tty) != 0)) { - un->un_flag |= UN_PWAIT; - count = 0; - goto out; - } - - /* - * Assure that whenever there is printer data in the output - * buffer, there always remains enough space after it to - * turn the printer off. - */ - space -= ch->ch_digi.digi_offlen; - - /* - * Output the printer on string. - */ - - if ((ch->ch_flag & CH_PRON) == 0) { - space -= ch->ch_digi.digi_onlen; - - if (space < 0) { - un->un_flag |= UN_EMPTY; - (ch->ch_nd)->nd_tx_work = 1; - count = 0; - goto out; - } - - drp_wmove(ch, 0, ch->ch_digi.digi_onstr, - ch->ch_digi.digi_onlen); - - ch->ch_flag |= CH_PRON; - } - - /* - * We measure printer CPS speed by incrementing - * ch_cpstime by (HZ / digi_maxcps) for every - * character we output, restricting output so - * that ch_cpstime never exceeds lbolt. - * - * However if output has not been done for some - * time, lbolt will grow to very much larger than - * ch_cpstime, which would allow essentially - * unlimited amounts of output until ch_cpstime - * finally caught up. To avoid this, we adjust - * cps_time when necessary so the difference - * between lbolt and ch_cpstime never results - * in sending more than digi_bufsize characters. - * - * This nicely models a printer with an internal - * buffer of digi_bufsize characters. - * - * Get the time between lbolt and ch->ch_cpstime; - */ - - tt = jiffies - ch->ch_cpstime; - - /* - * Compute the time required to send digi_bufsize - * characters. - */ - - mt = HZ * ch->ch_digi.digi_bufsize / ch->ch_digi.digi_maxcps; - - /* - * Compute the number of characters that can be sent - * without violating the time constraint. If the - * direct calculation of this number is bigger than - * digi_bufsize, limit the number to digi_bufsize, - * and adjust cpstime to match. - */ - - if ((clock_t)(tt + HZ) > (clock_t)(mt + HZ)) { - tmax = ch->ch_digi.digi_bufsize; - ch->ch_cpstime = jiffies - mt; - } else { - tmax = ch->ch_digi.digi_maxcps * tt / HZ; - } - - /* - * If the time constraint now binds, limit the transmit - * count accordingly, and tentatively arrange to be - * rescheduled based on time. - */ - - if (tmax < space) { - space = tmax; - un_flag = UN_TIME; - } - - /* - * Compute the total number of characters we can - * output before the total number of characters known - * to be in the output queue exceeds digi_maxchar. - */ - - tmax = (ch->ch_digi.digi_maxchar - - ((ch->ch_tin - ch->ch_tout) & TBUF_MASK) - - ((ch->ch_s_tin - ch->ch_s_tpos) & 0xffff)); - - - /* - * If the digi_maxchar constraint now holds, limit - * the transmit count accordingly, and arrange to - * be rescheduled when the queue becomes empty. - */ - - if (space > tmax) { - space = tmax; - un_flag = UN_EMPTY; - } - - } - /* - * Handle the terminal device. - */ - else { - - /* - * If the printer device is on, turn it off. - */ - - if ((ch->ch_flag & CH_PRON) != 0) { - - space -= ch->ch_digi.digi_offlen; - - drp_wmove(ch, 0, ch->ch_digi.digi_offstr, - ch->ch_digi.digi_offlen); - - ch->ch_flag &= ~CH_PRON; - } - } - - /* - * If space is 0 and its because the ch->tbuf - * is full, then Linux will handle a callback when queue - * space becomes available. - * tty_write returns count = 0 - */ - - if (space <= 0) { - /* the linux tty_io.c handles this if we return 0 */ - /* if (fp->flags & O_NONBLOCK) return -EAGAIN; */ - - un->un_flag |= UN_EMPTY; - (ch->ch_nd)->nd_tx_work = 1; - count = 0; - goto out; - } - - count = min(count, space); - - if (count > 0) { - - un->un_tbusy++; - - /* - * Copy the buffer contents to the ch_tbuf - * being careful to wrap around the circular queue - */ - - t = TBUF_MAX - ch->ch_tin; - n = count; - - if (n >= t) { - memcpy(ch->ch_tbuf + ch->ch_tin, buf, t); - if (nd->nd_dpa_debug && nd->nd_dpa_port == - PORT_NUM(MINOR(tty_devnum(un->un_tty)))) - dgrp_dpa_data(nd, 0, (char *) buf, t); - buf += t; - n -= t; - ch->ch_tin = 0; - sendcount += n; - } - - memcpy(ch->ch_tbuf + ch->ch_tin, buf, n); - if (nd->nd_dpa_debug && nd->nd_dpa_port == - PORT_NUM(MINOR(tty_devnum(un->un_tty)))) - dgrp_dpa_data(nd, 0, (char *) buf, n); - buf += n; - ch->ch_tin += n; - sendcount += n; - - un->un_tbusy--; - (ch->ch_nd)->nd_tx_work = 1; - if (ch->ch_edelay != DGRP_RTIME) { - (ch->ch_nd)->nd_tx_ready = 1; - wake_up_interruptible(&nd->nd_tx_waitq); - } - } - - ch->ch_txcount += count; - - if (IS_PRINT(MINOR(tty_devnum(tty)))) { - - /* - * Adjust ch_cpstime to account - * for the characters just output. - */ - - if (sendcount > 0) { - int cc = HZ * sendcount + ch->ch_cpsrem; - - ch->ch_cpstime += cc / ch->ch_digi.digi_maxcps; - ch->ch_cpsrem = cc % ch->ch_digi.digi_maxcps; - } - - /* - * If we are now waiting on time, schedule ourself - * back when we'll be able to send a block of - * digi_maxchar characters. - */ - - if ((un_flag & UN_TIME) != 0) { - ch->ch_waketime = (ch->ch_cpstime + - (ch->ch_digi.digi_maxchar * HZ / - ch->ch_digi.digi_maxcps)); - } - } - - /* - * If the printer unit is waiting for completion - * of terminal output, get him going again. - */ - - if ((ch->ch_pun.un_flag & UN_PWAIT) != 0) - (ch->ch_nd)->nd_tx_work = 1; - -out: - spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags); - - return count; -} - - -/* - * Put a character into ch->ch_buf - * - * - used by the line discipline for OPOST processing - */ - -static int dgrp_tty_put_char(struct tty_struct *tty, unsigned char new_char) -{ - struct un_struct *un; - struct ch_struct *ch; - ulong lock_flags; - int space; - int retval = 0; - - if (tty == NULL) - return 0; - - un = tty->driver_data; - if (!un) - return 0; - - ch = un->un_ch; - if (!ch) - return 0; - - if (ch->ch_state != CS_READY) - return 0; - - spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags); - - - /* - * If space is 0 and its because the ch->tbuf - * Warn and dump the character, there isn't anything else - * we can do about it. David_Fries@digi.com - */ - - space = (ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK; - - un->un_tbusy++; - - /* - * Output the printer on string if device is TXPrint. - */ - if (IS_PRINT(MINOR(tty_devnum(tty))) && (ch->ch_flag & CH_PRON) == 0) { - if (space < ch->ch_digi.digi_onlen) { - un->un_tbusy--; - goto out; - } - space -= ch->ch_digi.digi_onlen; - drp_wmove(ch, 0, ch->ch_digi.digi_onstr, - ch->ch_digi.digi_onlen); - ch->ch_flag |= CH_PRON; - } - - /* - * Output the printer off string if device is NOT TXPrint. - */ - - if (!IS_PRINT(MINOR(tty_devnum(tty))) && - ((ch->ch_flag & CH_PRON) != 0)) { - if (space < ch->ch_digi.digi_offlen) { - un->un_tbusy--; - goto out; - } - - space -= ch->ch_digi.digi_offlen; - drp_wmove(ch, 0, ch->ch_digi.digi_offstr, - ch->ch_digi.digi_offlen); - ch->ch_flag &= ~CH_PRON; - } - - if (!space) { - un->un_tbusy--; - goto out; - } - - /* - * Copy the character to the ch_tbuf being - * careful to wrap around the circular queue - */ - ch->ch_tbuf[ch->ch_tin] = new_char; - ch->ch_tin = (1 + ch->ch_tin) & TBUF_MASK; - - if (IS_PRINT(MINOR(tty_devnum(tty)))) { - - /* - * Adjust ch_cpstime to account - * for the character just output. - */ - - int cc = HZ + ch->ch_cpsrem; - - ch->ch_cpstime += cc / ch->ch_digi.digi_maxcps; - ch->ch_cpsrem = cc % ch->ch_digi.digi_maxcps; - - /* - * If we are now waiting on time, schedule ourself - * back when we'll be able to send a block of - * digi_maxchar characters. - */ - - ch->ch_waketime = (ch->ch_cpstime + - (ch->ch_digi.digi_maxchar * HZ / - ch->ch_digi.digi_maxcps)); - } - - - un->un_tbusy--; - (ch->ch_nd)->nd_tx_work = 1; - - retval = 1; -out: - spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags); - return retval; -} - - - -/* - * Flush TX buffer (make in == out) - * - * check tty_ioctl.c -- this is called after TCOFLUSH - */ -static void dgrp_tty_flush_buffer(struct tty_struct *tty) -{ - struct un_struct *un; - struct ch_struct *ch; - - if (!tty) - return; - un = tty->driver_data; - if (!un) - return; - - ch = un->un_ch; - if (!ch) - return; - - ch->ch_tout = ch->ch_tin; - /* do NOT do this here! */ - /* ch->ch_s_tpos = ch->ch_s_tin = 0; */ - - /* send the flush output command now */ - ch->ch_send |= RR_TX_FLUSH; - (ch->ch_nd)->nd_tx_ready = 1; - (ch->ch_nd)->nd_tx_work = 1; - wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq); - - if (waitqueue_active(&tty->write_wait)) - wake_up_interruptible(&tty->write_wait); - - tty_wakeup(tty); - -} - -/* - * Return space available in Tx buffer - * count = ( ch->ch_tout - ch->ch_tin ) mod (TBUF_MAX - 1) - */ -static int dgrp_tty_write_room(struct tty_struct *tty) -{ - struct un_struct *un; - struct ch_struct *ch; - int count; - - if (!tty) - return 0; - - un = tty->driver_data; - if (!un) - return 0; - - ch = un->un_ch; - if (!ch) - return 0; - - count = (ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK; - - /* We *MUST* check this, and return 0 if the Printer Unit cannot - * take any more data within its time constraints... If we don't - * return 0 and the printer has hit it time constraint, the ld will - * call us back doing a put_char, which cannot be rejected!!! - */ - if (IS_PRINT(MINOR(tty_devnum(tty)))) { - int un_flag = 0; - count = dgrp_calculate_txprint_bounds(ch, count, &un_flag); - if (count <= 0) - count = 0; - - ch->ch_pun.un_flag |= un_flag; - (ch->ch_nd)->nd_tx_work = 1; - } - - return count; -} - -/* - * Return number of characters that have not been transmitted yet. - * chars_in_buffer = ( ch->ch_tin - ch->ch_tout ) mod (TBUF_MAX - 1) - * + ( ch->ch_s_tin - ch->ch_s_tout ) mod (0xffff) - * = number of characters "in transit" - * - * Remember that sequence number math is always with a sixteen bit - * mask, not the TBUF_MASK. - */ - -static int dgrp_tty_chars_in_buffer(struct tty_struct *tty) -{ - struct un_struct *un; - struct ch_struct *ch; - int count; - int count1; - - if (!tty) - return 0; - - un = tty->driver_data; - if (!un) - return 0; - - ch = un->un_ch; - if (!ch) - return 0; - - count1 = count = (ch->ch_tin - ch->ch_tout) & TBUF_MASK; - count += (ch->ch_s_tin - ch->ch_s_tpos) & 0xffff; - /* one for tbuf, one for the PS */ - - /* - * If we are busy transmitting add 1 - */ - count += un->un_tbusy; - - return count; -} - - -/***************************************************************************** - * - * Helper applications for dgrp_tty_ioctl() - * - ***************************************************************************** - */ - - -/** - * ch_to_tty_flags() -- convert channel flags to termio flags - * @ch_flag: Digi channel flags - * @flagtype: type of ch_flag (iflag, oflag or cflag) - * - * take the channel flags of the specified type and return the - * corresponding termio flag - */ -static tcflag_t ch_to_tty_flags(ushort ch_flag, char flagtype) -{ - tcflag_t retval = 0; - - switch (flagtype) { - case 'i': - retval = ((ch_flag & IF_IGNBRK) ? IGNBRK : 0) - | ((ch_flag & IF_BRKINT) ? BRKINT : 0) - | ((ch_flag & IF_IGNPAR) ? IGNPAR : 0) - | ((ch_flag & IF_PARMRK) ? PARMRK : 0) - | ((ch_flag & IF_INPCK) ? INPCK : 0) - | ((ch_flag & IF_ISTRIP) ? ISTRIP : 0) - | ((ch_flag & IF_IXON) ? IXON : 0) - | ((ch_flag & IF_IXANY) ? IXANY : 0) - | ((ch_flag & IF_IXOFF) ? IXOFF : 0); - break; - - case 'o': - retval = ((ch_flag & OF_OLCUC) ? OLCUC : 0) - | ((ch_flag & OF_ONLCR) ? ONLCR : 0) - | ((ch_flag & OF_OCRNL) ? OCRNL : 0) - | ((ch_flag & OF_ONOCR) ? ONOCR : 0) - | ((ch_flag & OF_ONLRET) ? ONLRET : 0) - /* | ((ch_flag & OF_OTAB3) ? OFILL : 0) */ - | ((ch_flag & OF_TABDLY) ? TABDLY : 0); - break; - - case 'c': - retval = ((ch_flag & CF_CSTOPB) ? CSTOPB : 0) - | ((ch_flag & CF_CREAD) ? CREAD : 0) - | ((ch_flag & CF_PARENB) ? PARENB : 0) - | ((ch_flag & CF_PARODD) ? PARODD : 0) - | ((ch_flag & CF_HUPCL) ? HUPCL : 0); - - switch (ch_flag & CF_CSIZE) { - case CF_CS5: - retval |= CS5; - break; - case CF_CS6: - retval |= CS6; - break; - case CF_CS7: - retval |= CS7; - break; - case CF_CS8: - retval |= CS8; - break; - default: - retval |= CS8; - break; - } - break; - case 'x': - break; - case 'l': - break; - default: - return 0; - } - - return retval; -} - - -/** - * tty_to_ch_flags() -- convert termio flags to digi channel flags - * @tty: pointer to a TTY structure holding flag to be converted - * @flagtype: identifies which flag (iflags, oflags, or cflags) should - * be converted - * - * take the termio flag of the specified type and return the - * corresponding Digi version of the flags - */ -static ushort tty_to_ch_flags(struct tty_struct *tty, char flagtype) -{ - ushort retval = 0; - tcflag_t tflag = 0; - - switch (flagtype) { - case 'i': - tflag = tty->termios.c_iflag; - retval = (I_IGNBRK(tty) ? IF_IGNBRK : 0) - | (I_BRKINT(tty) ? IF_BRKINT : 0) - | (I_IGNPAR(tty) ? IF_IGNPAR : 0) - | (I_PARMRK(tty) ? IF_PARMRK : 0) - | (I_INPCK(tty) ? IF_INPCK : 0) - | (I_ISTRIP(tty) ? IF_ISTRIP : 0) - | (I_IXON(tty) ? IF_IXON : 0) - | (I_IXANY(tty) ? IF_IXANY : 0) - | (I_IXOFF(tty) ? IF_IXOFF : 0); - break; - case 'o': - tflag = tty->termios.c_oflag; - /* - * If OPOST is set, then do the post processing in the - * firmware by setting all the processing flags on. - * If ~OPOST, then make sure we are not doing any - * output processing!! - */ - if (!O_OPOST(tty)) - retval = 0; - else - retval = (O_OLCUC(tty) ? OF_OLCUC : 0) - | (O_ONLCR(tty) ? OF_ONLCR : 0) - | (O_OCRNL(tty) ? OF_OCRNL : 0) - | (O_ONOCR(tty) ? OF_ONOCR : 0) - | (O_ONLRET(tty) ? OF_ONLRET : 0) - /* | (O_OFILL(tty) ? OF_TAB3 : 0) */ - | (O_TABDLY(tty) ? OF_TABDLY : 0); - break; - case 'c': - tflag = tty->termios.c_cflag; - retval = (C_CSTOPB(tty) ? CF_CSTOPB : 0) - | (C_CREAD(tty) ? CF_CREAD : 0) - | (C_PARENB(tty) ? CF_PARENB : 0) - | (C_PARODD(tty) ? CF_PARODD : 0) - | (C_HUPCL(tty) ? CF_HUPCL : 0); - switch (C_CSIZE(tty)) { - case CS8: - retval |= CF_CS8; - break; - case CS7: - retval |= CF_CS7; - break; - case CS6: - retval |= CF_CS6; - break; - case CS5: - retval |= CF_CS5; - break; - default: - retval |= CF_CS8; - break; - } - break; - case 'x': - break; - case 'l': - break; - default: - return 0; - } - - return retval; -} - - -static int dgrp_tty_send_break(struct tty_struct *tty, int msec) -{ - struct un_struct *un; - struct ch_struct *ch; - int ret = -EIO; - - if (!tty) - return ret; - - un = tty->driver_data; - if (!un) - return ret; - - ch = un->un_ch; - if (!ch) - return ret; - - dgrp_send_break(ch, msec); - return 0; -} - - -/* - * This routine sends a break character out the serial port. - * - * duration is in 1/1000's of a second - */ -static int dgrp_send_break(struct ch_struct *ch, int msec) -{ - ulong x; - - wait_event_interruptible(ch->ch_flag_wait, - ((ch->ch_flag & CH_TX_BREAK) == 0)); - ch->ch_break_time += max(msec, 250); - ch->ch_send |= RR_TX_BREAK; - ch->ch_flag |= CH_TX_BREAK; - (ch->ch_nd)->nd_tx_work = 1; - - x = (msec * HZ) / 1000; - wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq); - - return 0; -} - - -/* - * Return modem signals to ld. - */ -static int dgrp_tty_tiocmget(struct tty_struct *tty) -{ - unsigned int mlast; - struct un_struct *un = tty->driver_data; - struct ch_struct *ch; - - if (!un) - return -ENODEV; - - ch = un->un_ch; - if (!ch) - return -ENODEV; - - mlast = ((ch->ch_s_mlast & ~(DM_RTS | DM_DTR)) | - (ch->ch_mout & (DM_RTS | DM_DTR))); - - /* defined in /usr/include/asm/termios.h */ - mlast = ((mlast & DM_RTS) ? TIOCM_RTS : 0) - | ((mlast & DM_DTR) ? TIOCM_DTR : 0) - | ((mlast & DM_CD) ? TIOCM_CAR : 0) - | ((mlast & DM_RI) ? TIOCM_RNG : 0) - | ((mlast & DM_DSR) ? TIOCM_DSR : 0) - | ((mlast & DM_CTS) ? TIOCM_CTS : 0); - - return mlast; -} - - -/* - * Set modem lines - */ -static int dgrp_tty_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - ulong lock_flags; - struct un_struct *un = tty->driver_data; - struct ch_struct *ch; - - if (!un) - return -ENODEV; - - ch = un->un_ch; - if (!ch) - return -ENODEV; - - if (set & TIOCM_RTS) - ch->ch_mout |= DM_RTS; - - if (set & TIOCM_DTR) - ch->ch_mout |= DM_DTR; - - if (clear & TIOCM_RTS) - ch->ch_mout &= ~(DM_RTS); - - if (clear & TIOCM_DTR) - ch->ch_mout &= ~(DM_DTR); - - spin_lock_irqsave(&(ch->ch_nd)->nd_lock, lock_flags); - ch->ch_flag |= CH_PARAM; - (ch->ch_nd)->nd_tx_work = 1; - wake_up_interruptible(&ch->ch_flag_wait); - - spin_unlock_irqrestore(&(ch->ch_nd)->nd_lock, lock_flags); - - return 0; -} - - - -/* - * Get current modem status - */ -static int get_modem_info(struct ch_struct *ch, unsigned int *value) -{ - unsigned int mlast; - - mlast = ((ch->ch_s_mlast & ~(DM_RTS | DM_DTR)) | - (ch->ch_mout & (DM_RTS | DM_DTR))); - - /* defined in /usr/include/asm/termios.h */ - mlast = ((mlast & DM_RTS) ? TIOCM_RTS : 0) - | ((mlast & DM_DTR) ? TIOCM_DTR : 0) - | ((mlast & DM_CD) ? TIOCM_CAR : 0) - | ((mlast & DM_RI) ? TIOCM_RNG : 0) - | ((mlast & DM_DSR) ? TIOCM_DSR : 0) - | ((mlast & DM_CTS) ? TIOCM_CTS : 0); - return put_user(mlast, (unsigned int __user *) value); -} - -/* - * Set modem lines - */ -static int set_modem_info(struct ch_struct *ch, unsigned int command, - unsigned int *value) -{ - int error; - unsigned int arg; - int mval = 0; - ulong lock_flags; - - error = access_ok(VERIFY_READ, (void __user *) value, sizeof(int)); - if (error == 0) - return -EFAULT; - - if (get_user(arg, (unsigned int __user *) value)) - return -EFAULT; - mval |= ((arg & TIOCM_RTS) ? DM_RTS : 0) - | ((arg & TIOCM_DTR) ? DM_DTR : 0); - - switch (command) { - case TIOCMBIS: /* set flags */ - ch->ch_mout |= mval; - break; - case TIOCMBIC: /* clear flags */ - ch->ch_mout &= ~mval; - break; - case TIOCMSET: - ch->ch_mout = mval; - break; - default: - return -EINVAL; - } - - spin_lock_irqsave(&(ch->ch_nd)->nd_lock, lock_flags); - - ch->ch_flag |= CH_PARAM; - (ch->ch_nd)->nd_tx_work = 1; - wake_up_interruptible(&ch->ch_flag_wait); - - spin_unlock_irqrestore(&(ch->ch_nd)->nd_lock, lock_flags); - - return 0; -} - - -/* - * Assign the custom baud rate to the channel structure - */ -static void dgrp_set_custom_speed(struct ch_struct *ch, int newrate) -{ - int testdiv; - int testrate_high; - int testrate_low; - - int deltahigh, deltalow; - - if (newrate < 0) - newrate = 0; - - /* - * Since the divisor is stored in a 16-bit integer, we make sure - * we don't allow any rates smaller than a 16-bit integer would allow. - * And of course, rates above the dividend won't fly. - */ - if (newrate && newrate < ((PORTSERVER_DIVIDEND / 0xFFFF) + 1)) - newrate = ((PORTSERVER_DIVIDEND / 0xFFFF) + 1); - if (newrate && newrate > PORTSERVER_DIVIDEND) - newrate = PORTSERVER_DIVIDEND; - - while (newrate > 0) { - testdiv = PORTSERVER_DIVIDEND / newrate; - - /* - * If we try to figure out what rate the PortServer would use - * with the test divisor, it will be either equal or higher - * than the requested baud rate. If we then determine the - * rate with a divisor one higher, we will get the next lower - * supported rate below the requested. - */ - testrate_high = PORTSERVER_DIVIDEND / testdiv; - testrate_low = PORTSERVER_DIVIDEND / (testdiv + 1); - - /* - * If the rate for the requested divisor is correct, just - * use it and be done. - */ - if (testrate_high == newrate) - break; - - /* - * Otherwise, pick the rate that is closer (i.e. whichever rate - * has a smaller delta). - */ - deltahigh = testrate_high - newrate; - deltalow = newrate - testrate_low; - - if (deltahigh < deltalow) - newrate = testrate_high; - else - newrate = testrate_low; - - break; - } - - ch->ch_custom_speed = newrate; - - drp_param(ch); - - return; -} - - -/* - # dgrp_tty_digiseta() - * - * Ioctl to set the information from ditty. - * - * NOTE: DIGI_IXON, DSRPACE, DCDPACE, and DTRPACE are unsupported. JAR 990922 - */ -static int dgrp_tty_digiseta(struct tty_struct *tty, - struct digi_struct *new_info) -{ - struct un_struct *un = tty->driver_data; - struct ch_struct *ch; - - if (!un) - return -ENODEV; - - ch = un->un_ch; - if (!ch) - return -ENODEV; - - if (copy_from_user(&ch->ch_digi, (void __user *) new_info, - sizeof(struct digi_struct))) - return -EFAULT; - - if ((ch->ch_digi.digi_flags & RTSPACE) || - (ch->ch_digi.digi_flags & CTSPACE)) - tty->termios.c_cflag |= CRTSCTS; - else - tty->termios.c_cflag &= ~CRTSCTS; - - if (ch->ch_digi.digi_maxcps < 1) - ch->ch_digi.digi_maxcps = 1; - - if (ch->ch_digi.digi_maxcps > 10000) - ch->ch_digi.digi_maxcps = 10000; - - if (ch->ch_digi.digi_bufsize < 10) - ch->ch_digi.digi_bufsize = 10; - - if (ch->ch_digi.digi_maxchar < 1) - ch->ch_digi.digi_maxchar = 1; - - if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize) - ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize; - - if (ch->ch_digi.digi_onlen > DIGI_PLEN) - ch->ch_digi.digi_onlen = DIGI_PLEN; - - if (ch->ch_digi.digi_offlen > DIGI_PLEN) - ch->ch_digi.digi_offlen = DIGI_PLEN; - - /* make the changes now */ - drp_param(ch); - - return 0; -} - - - -/* - * dgrp_tty_digigetedelay() - * - * Ioctl to get the current edelay setting. - * - * - * - */ -static int dgrp_tty_digigetedelay(struct tty_struct *tty, int *retinfo) -{ - struct un_struct *un; - struct ch_struct *ch; - int tmp; - - if (!retinfo) - return -EFAULT; - - if (!tty || tty->magic != TTY_MAGIC) - return -EFAULT; - - un = tty->driver_data; - - if (!un) - return -ENODEV; - - ch = un->un_ch; - if (!ch) - return -ENODEV; - - tmp = ch->ch_edelay; - - if (copy_to_user((void __user *) retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - - return 0; -} - - -/* - * dgrp_tty_digisetedelay() - * - * Ioctl to set the EDELAY setting - * - */ -static int dgrp_tty_digisetedelay(struct tty_struct *tty, int *new_info) -{ - struct un_struct *un; - struct ch_struct *ch; - int new_digi; - - if (!tty || tty->magic != TTY_MAGIC) - return -EFAULT; - - un = tty->driver_data; - - if (!un) - return -ENODEV; - - ch = un->un_ch; - if (!ch) - return -ENODEV; - - if (copy_from_user(&new_digi, (void __user *)new_info, sizeof(int))) - return -EFAULT; - - ch->ch_edelay = new_digi; - - /* make the changes now */ - drp_param(ch); - - return 0; -} - - -/* - * The usual assortment of ioctl's - * - * note: use tty_check_change to make sure that we are not - * changing the state of a terminal when we are not a process - * in the forground. See tty_io.c - * rc = tty_check_change(tty); - * if (rc) return rc; - */ -static int dgrp_tty_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct un_struct *un; - struct ch_struct *ch; - int rc; - struct digiflow_struct dflow; - - if (!tty) - return -ENODEV; - - un = tty->driver_data; - if (!un) - return -ENODEV; - - ch = un->un_ch; - if (!ch) - return -ENODEV; - - switch (cmd) { - - /* - * Here are all the standard ioctl's that we MUST implement - */ - - case TCSBRK: - /* - * TCSBRK is SVID version: non-zero arg --> no break - * this behaviour is exploited by tcdrain(). - * - * According to POSIX.1 spec (7.2.2.1.2) breaks should be - * between 0.25 and 0.5 seconds - */ - - rc = tty_check_change(tty); - if (rc) - return rc; - tty_wait_until_sent(tty, 0); - - if (!arg) - rc = dgrp_send_break(ch, 250); /* 1/4 second */ - - if (dgrp_tty_chars_in_buffer(tty) != 0) - return -EINTR; - - return 0; - - case TCSBRKP: - /* support for POSIX tcsendbreak() - * - * According to POSIX.1 spec (7.2.2.1.2) breaks should be - * between 0.25 and 0.5 seconds so we'll ask for something - * in the middle: 0.375 seconds. - */ - rc = tty_check_change(tty); - if (rc) - return rc; - tty_wait_until_sent(tty, 0); - - rc = dgrp_send_break(ch, arg ? arg*250 : 250); - - if (dgrp_tty_chars_in_buffer(tty) != 0) - return -EINTR; - return 0; - - case TIOCSBRK: - rc = tty_check_change(tty); - if (rc) - return rc; - tty_wait_until_sent(tty, 0); - - /* - * RealPort doesn't support turning on a break unconditionally. - * The RealPort device will stop sending a break automatically - * after the specified time value that we send in. - */ - rc = dgrp_send_break(ch, 250); /* 1/4 second */ - - if (dgrp_tty_chars_in_buffer(tty) != 0) - return -EINTR; - return 0; - - case TIOCCBRK: - /* - * RealPort doesn't support turning off a break unconditionally. - * The RealPort device will stop sending a break automatically - * after the specified time value that was sent when turning on - * the break. - */ - return 0; - - case TIOCMGET: - rc = access_ok(VERIFY_WRITE, (void __user *) arg, - sizeof(unsigned int)); - if (rc == 0) - return -EFAULT; - return get_modem_info(ch, (unsigned int *) arg); - - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - return set_modem_info(ch, cmd, (unsigned int *) arg); - - /* - * Here are any additional ioctl's that we want to implement - */ - - case TCFLSH: - /* - * The linux tty driver doesn't have a flush - * input routine for the driver, assuming all backed - * up data is in the line disc. buffers. However, - * we all know that's not the case. Here, we - * act on the ioctl, but then lie and say we didn't - * so the line discipline will process the flush - * also. - */ - rc = tty_check_change(tty); - if (rc) - return rc; - - switch (arg) { - case TCIFLUSH: - case TCIOFLUSH: - /* only flush input if this is the only open unit */ - if (!IS_PRINT(MINOR(tty_devnum(tty)))) { - ch->ch_rout = ch->ch_rin; - ch->ch_send |= RR_RX_FLUSH; - (ch->ch_nd)->nd_tx_work = 1; - (ch->ch_nd)->nd_tx_ready = 1; - wake_up_interruptible( - &(ch->ch_nd)->nd_tx_waitq); - } - if (arg == TCIFLUSH) - break; - - case TCOFLUSH: /* flush output, or the receive buffer */ - /* - * This is handled in the tty_ioctl.c code - * calling tty_flush_buffer - */ - break; - - default: - /* POSIX.1 says return EINVAL if we got a bad arg */ - return -EINVAL; - } - /* pretend we didn't recognize this IOCTL */ - return -ENOIOCTLCMD; - -#ifdef TIOCGETP - case TIOCGETP: -#endif - /***************************************** - Linux HPUX Function - TCSETA TCSETA - set the termios - TCSETAF TCSETAF - wait for drain first, then set termios - TCSETAW TCSETAW - wait for drain, - flush the input queue, then set termios - - looking at the tty_ioctl code, these command all call our - tty_set_termios at the driver's end, when a TCSETA* is sent, - it is expecting the tty to have a termio structure, - NOT a termios structure. These two structures differ in size - and the tty_ioctl code does a conversion before processing them both. - - we should treat the TCSETAW TCSETAF ioctls the same, and let - the tty_ioctl code do the conversion stuff. - - TCSETS - TCSETSF (none) - TCSETSW - - the associated tty structure has a termios structure. - *****************************************/ - - case TCGETS: - case TCGETA: - return -ENOIOCTLCMD; - - case TCSETAW: - case TCSETAF: - case TCSETSF: - case TCSETSW: - /* - * The linux tty driver doesn't have a flush - * input routine for the driver, assuming all backed - * up data is in the line disc. buffers. However, - * we all know that's not the case. Here, we - * act on the ioctl, but then lie and say we didn't - * so the line discipline will process the flush - * also. - */ - - /* - * Also, now that we have TXPrint, we have to check - * if this is the TXPrint device and the terminal - * device is open. If so, do NOT run check_change, - * as the terminal device is ALWAYS the parent. - */ - if (!IS_PRINT(MINOR(tty_devnum(tty))) || - !ch->ch_tun.un_open_count) { - rc = tty_check_change(tty); - if (rc) - return rc; - } - - /* wait for all the characters in tbuf to drain */ - tty_wait_until_sent(tty, 0); - - if ((cmd == TCSETSF) || (cmd == TCSETAF)) { - /* flush the contents of the rbuf queue */ - /* TODO: check if this is print device? */ - ch->ch_send |= RR_RX_FLUSH; - (ch->ch_nd)->nd_tx_ready = 1; - (ch->ch_nd)->nd_tx_work = 1; - wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq); - /* do we need to do this? just to be safe! */ - ch->ch_rout = ch->ch_rin; - } - - /* pretend we didn't recognize this */ - return -ENOIOCTLCMD; - - case TCXONC: - /* - * The Linux Line Discipline (LD) would do this for us if we - * let it, but we have the special firmware options to do this - * the "right way" regardless of hardware or software flow - * control so we'll do it outselves instead of letting the LD - * do it. - */ - rc = tty_check_change(tty); - if (rc) - return rc; - - switch (arg) { - case TCOON: - dgrp_tty_start(tty); - return 0; - case TCOOFF: - dgrp_tty_stop(tty); - return 0; - case TCION: - dgrp_tty_input_start(tty); - return 0; - case TCIOFF: - dgrp_tty_input_stop(tty); - return 0; - default: - return -EINVAL; - } - - case DIGI_GETA: - /* get information for ditty */ - if (copy_to_user((struct digi_struct __user *) arg, - &ch->ch_digi, sizeof(struct digi_struct))) - return -EFAULT; - break; - - case DIGI_SETAW: - case DIGI_SETAF: - /* wait for all the characters in tbuf to drain */ - tty_wait_until_sent(tty, 0); - - if (cmd == DIGI_SETAF) { - /* flush the contents of the rbuf queue */ - /* send down a packet with RR_RX_FLUSH set */ - ch->ch_send |= RR_RX_FLUSH; - (ch->ch_nd)->nd_tx_ready = 1; - (ch->ch_nd)->nd_tx_work = 1; - wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq); - /* do we need to do this? just to be safe! */ - ch->ch_rout = ch->ch_rin; - } - - /* pretend we didn't recognize this */ - /* fall-through */ - - case DIGI_SETA: - return dgrp_tty_digiseta(tty, (struct digi_struct *) arg); - - case DIGI_SEDELAY: - return dgrp_tty_digisetedelay(tty, (int *) arg); - - case DIGI_GEDELAY: - return dgrp_tty_digigetedelay(tty, (int *) arg); - - case DIGI_GETFLOW: - case DIGI_GETAFLOW: - if (cmd == (DIGI_GETFLOW)) { - dflow.startc = tty->termios.c_cc[VSTART]; - dflow.stopc = tty->termios.c_cc[VSTOP]; - } else { - dflow.startc = ch->ch_xxon; - dflow.stopc = ch->ch_xxoff; - } - - if (copy_to_user((char __user *)arg, &dflow, sizeof(dflow))) - return -EFAULT; - break; - - case DIGI_SETFLOW: - case DIGI_SETAFLOW: - - if (copy_from_user(&dflow, (char __user *)arg, sizeof(dflow))) - return -EFAULT; - - if (cmd == (DIGI_SETFLOW)) { - tty->termios.c_cc[VSTART] = dflow.startc; - tty->termios.c_cc[VSTOP] = dflow.stopc; - } else { - ch->ch_xxon = dflow.startc; - ch->ch_xxoff = dflow.stopc; - } - break; - - case DIGI_GETCUSTOMBAUD: - if (put_user(ch->ch_custom_speed, (unsigned int __user *) arg)) - return -EFAULT; - break; - - case DIGI_SETCUSTOMBAUD: - { - int new_rate; - - if (get_user(new_rate, (unsigned int __user *) arg)) - return -EFAULT; - dgrp_set_custom_speed(ch, new_rate); - - break; - } - - default: - return -ENOIOCTLCMD; - } - - return 0; -} - -/* - * This routine allows the tty driver to be notified when - * the device's termios setting have changed. Note that we - * should be prepared to accept the case where old == NULL - * and try to do something rational. - * - * So we need to make sure that our copies of ch_oflag, - * ch_clag, and ch_iflag reflect the tty->termios flags. - */ -static void dgrp_tty_set_termios(struct tty_struct *tty, struct ktermios *old) -{ - struct ktermios *ts; - struct ch_struct *ch; - struct un_struct *un; - - /* seems silly, but we have to check all these! */ - if (!tty) - return; - - un = tty->driver_data; - if (!un) - return; - - ts = &tty->termios; - - ch = un->un_ch; - if (!ch) - return; - - drp_param(ch); - - /* the CLOCAL flag has just been set */ - if (!(old->c_cflag & CLOCAL) && C_CLOCAL(tty)) - wake_up_interruptible(&un->un_open_wait); -} - - -/* - * Throttle receiving data. We just set a bit and stop reading - * data out of the channel buffer. It will back up and the - * FEP will do whatever is necessary to stop the far end. - */ -static void dgrp_tty_throttle(struct tty_struct *tty) -{ - struct ch_struct *ch; - - if (!tty) - return; - - ch = ((struct un_struct *) tty->driver_data)->un_ch; - if (!ch) - return; - - ch->ch_flag |= CH_RXSTOP; -} - - -static void dgrp_tty_unthrottle(struct tty_struct *tty) -{ - struct ch_struct *ch; - - if (!tty) - return; - - ch = ((struct un_struct *) tty->driver_data)->un_ch; - if (!ch) - return; - - ch->ch_flag &= ~CH_RXSTOP; -} - -/* - * Stop the transmitter - */ -static void dgrp_tty_stop(struct tty_struct *tty) -{ - struct ch_struct *ch; - - if (!tty) - return; - - ch = ((struct un_struct *) tty->driver_data)->un_ch; - if (!ch) - return; - - ch->ch_send |= RR_TX_STOP; - ch->ch_send &= ~RR_TX_START; - - /* make the change NOW! */ - (ch->ch_nd)->nd_tx_ready = 1; - if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq)) - wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq); -} - -/* - * Start the transmitter - */ -static void dgrp_tty_start(struct tty_struct *tty) -{ - struct ch_struct *ch; - - if (!tty) - return; - - ch = ((struct un_struct *) tty->driver_data)->un_ch; - if (!ch) - return; - - /* TODO: don't do anything if the transmitter is not stopped */ - - ch->ch_send |= RR_TX_START; - ch->ch_send &= ~RR_TX_STOP; - - /* make the change NOW! */ - (ch->ch_nd)->nd_tx_ready = 1; - (ch->ch_nd)->nd_tx_work = 1; - if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq)) - wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq); - -} - -/* - * Stop the receiver - */ -static void dgrp_tty_input_stop(struct tty_struct *tty) -{ - struct ch_struct *ch; - - if (!tty) - return; - - ch = ((struct un_struct *) tty->driver_data)->un_ch; - if (!ch) - return; - - ch->ch_send |= RR_RX_STOP; - ch->ch_send &= ~RR_RX_START; - (ch->ch_nd)->nd_tx_ready = 1; - if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq)) - wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq); - -} - - -static void dgrp_tty_send_xchar(struct tty_struct *tty, char c) -{ - struct un_struct *un; - struct ch_struct *ch; - - if (!tty) - return; - - un = tty->driver_data; - if (!un) - return; - - ch = un->un_ch; - if (!ch) - return; - if (c == STOP_CHAR(tty)) - ch->ch_send |= RR_RX_STOP; - else if (c == START_CHAR(tty)) - ch->ch_send |= RR_RX_START; - - ch->ch_nd->nd_tx_ready = 1; - ch->ch_nd->nd_tx_work = 1; - - return; -} - - -static void dgrp_tty_input_start(struct tty_struct *tty) -{ - struct ch_struct *ch; - - if (!tty) - return; - - ch = ((struct un_struct *) tty->driver_data)->un_ch; - if (!ch) - return; - - ch->ch_send |= RR_RX_START; - ch->ch_send &= ~RR_RX_STOP; - (ch->ch_nd)->nd_tx_ready = 1; - (ch->ch_nd)->nd_tx_work = 1; - if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq)) - wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq); - -} - - -/* - * Hangup the port. Like a close, but don't wait for output - * to drain. - * - * How do we close all the channels that are open? - */ -static void dgrp_tty_hangup(struct tty_struct *tty) -{ - struct ch_struct *ch; - struct nd_struct *nd; - struct un_struct *un; - - if (!tty) - return; - - un = tty->driver_data; - if (!un) - return; - - ch = un->un_ch; - if (!ch) - return; - - nd = ch->ch_nd; - - if (C_HUPCL(tty)) { - /* LOWER DTR */ - ch->ch_mout &= ~DM_DTR; - /* Don't do this here */ - /* ch->ch_flag |= CH_HANGUP; */ - ch->ch_nd->nd_tx_ready = 1; - ch->ch_nd->nd_tx_work = 1; - if (waitqueue_active(&ch->ch_flag_wait)) - wake_up_interruptible(&ch->ch_flag_wait); - } - -} - -/************************************************************************/ -/* */ -/* TTY Initialization/Cleanup Functions */ -/* */ -/************************************************************************/ - -/* - * Uninitialize the TTY portion of the supplied node. Free all - * memory and resources associated with this node. Do it in reverse - * allocation order: this might possibly result in less fragmentation - * of memory, though I don't know this for sure. - */ -void -dgrp_tty_uninit(struct nd_struct *nd) -{ - unsigned int i; - char id[3]; - - ID_TO_CHAR(nd->nd_ID, id); - - if (nd->nd_ttdriver_flags & SERIAL_TTDRV_REG) { - tty_unregister_driver(nd->nd_serial_ttdriver); - - kfree(nd->nd_serial_ttdriver->ttys); - nd->nd_serial_ttdriver->ttys = NULL; - - put_tty_driver(nd->nd_serial_ttdriver); - nd->nd_ttdriver_flags &= ~SERIAL_TTDRV_REG; - } - - if (nd->nd_ttdriver_flags & CALLOUT_TTDRV_REG) { - tty_unregister_driver(nd->nd_callout_ttdriver); - - kfree(nd->nd_callout_ttdriver->ttys); - nd->nd_callout_ttdriver->ttys = NULL; - - put_tty_driver(nd->nd_callout_ttdriver); - nd->nd_ttdriver_flags &= ~CALLOUT_TTDRV_REG; - } - - if (nd->nd_ttdriver_flags & XPRINT_TTDRV_REG) { - tty_unregister_driver(nd->nd_xprint_ttdriver); - - kfree(nd->nd_xprint_ttdriver->ttys); - nd->nd_xprint_ttdriver->ttys = NULL; - - put_tty_driver(nd->nd_xprint_ttdriver); - nd->nd_ttdriver_flags &= ~XPRINT_TTDRV_REG; - } - for (i = 0; i < CHAN_MAX; i++) - tty_port_destroy(&nd->nd_chan[i].port); -} - - - -/* - * Initialize the TTY portion of the supplied node. - */ -int -dgrp_tty_init(struct nd_struct *nd) -{ - char id[3]; - int rc; - int i; - - ID_TO_CHAR(nd->nd_ID, id); - - /* - * Initialize the TTDRIVER structures. - */ - - nd->nd_serial_ttdriver = alloc_tty_driver(CHAN_MAX); - if (!nd->nd_serial_ttdriver) - return -ENOMEM; - - sprintf(nd->nd_serial_name, "tty_dgrp_%s_", id); - - nd->nd_serial_ttdriver->owner = THIS_MODULE; - nd->nd_serial_ttdriver->name = nd->nd_serial_name; - nd->nd_serial_ttdriver->name_base = 0; - nd->nd_serial_ttdriver->major = 0; - nd->nd_serial_ttdriver->minor_start = 0; - nd->nd_serial_ttdriver->type = TTY_DRIVER_TYPE_SERIAL; - nd->nd_serial_ttdriver->subtype = SERIAL_TYPE_NORMAL; - nd->nd_serial_ttdriver->init_termios = DefaultTermios; - nd->nd_serial_ttdriver->driver_name = "dgrp"; - nd->nd_serial_ttdriver->flags = (TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | - TTY_DRIVER_HARDWARE_BREAK); - - /* The kernel wants space to store pointers to tty_structs. */ - nd->nd_serial_ttdriver->ttys = - kzalloc(CHAN_MAX * sizeof(struct tty_struct *), GFP_KERNEL); - if (!nd->nd_serial_ttdriver->ttys) - return -ENOMEM; - - tty_set_operations(nd->nd_serial_ttdriver, &dgrp_tty_ops); - - if (!(nd->nd_ttdriver_flags & SERIAL_TTDRV_REG)) { - /* - * Register tty devices - */ - rc = tty_register_driver(nd->nd_serial_ttdriver); - if (rc < 0) { - /* - * If errno is EBUSY, this means there are no more - * slots available to have us auto-majored. - * (Which is currently supported up to 256) - * - * We can still request majors above 256, - * we just have to do it manually. - */ - if (rc == -EBUSY) { - int i; - int max_majors = 1U << (32 - MINORBITS); - for (i = 256; i < max_majors; i++) { - nd->nd_serial_ttdriver->major = i; - rc = tty_register_driver - (nd->nd_serial_ttdriver); - if (rc >= 0) - break; - } - /* Really fail now, since we ran out - * of majors to try. */ - if (i == max_majors) - return rc; - - } else { - return rc; - } - } - nd->nd_ttdriver_flags |= SERIAL_TTDRV_REG; - } - - nd->nd_callout_ttdriver = alloc_tty_driver(CHAN_MAX); - if (!nd->nd_callout_ttdriver) - return -ENOMEM; - - sprintf(nd->nd_callout_name, "cu_dgrp_%s_", id); - - nd->nd_callout_ttdriver->owner = THIS_MODULE; - nd->nd_callout_ttdriver->name = nd->nd_callout_name; - nd->nd_callout_ttdriver->name_base = 0; - nd->nd_callout_ttdriver->major = nd->nd_serial_ttdriver->major; - nd->nd_callout_ttdriver->minor_start = 0x40; - nd->nd_callout_ttdriver->type = TTY_DRIVER_TYPE_SERIAL; - nd->nd_callout_ttdriver->subtype = SERIAL_TYPE_CALLOUT; - nd->nd_callout_ttdriver->init_termios = DefaultTermios; - nd->nd_callout_ttdriver->driver_name = "dgrp"; - nd->nd_callout_ttdriver->flags = (TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | - TTY_DRIVER_HARDWARE_BREAK); - - /* The kernel wants space to store pointers to tty_structs. */ - nd->nd_callout_ttdriver->ttys = - kzalloc(CHAN_MAX * sizeof(struct tty_struct *), GFP_KERNEL); - if (!nd->nd_callout_ttdriver->ttys) - return -ENOMEM; - - tty_set_operations(nd->nd_callout_ttdriver, &dgrp_tty_ops); - - if (dgrp_register_cudevices) { - if (!(nd->nd_ttdriver_flags & CALLOUT_TTDRV_REG)) { - /* - * Register cu devices - */ - rc = tty_register_driver(nd->nd_callout_ttdriver); - if (rc < 0) - return rc; - nd->nd_ttdriver_flags |= CALLOUT_TTDRV_REG; - } - } - - - nd->nd_xprint_ttdriver = alloc_tty_driver(CHAN_MAX); - if (!nd->nd_xprint_ttdriver) - return -ENOMEM; - - sprintf(nd->nd_xprint_name, "pr_dgrp_%s_", id); - - nd->nd_xprint_ttdriver->owner = THIS_MODULE; - nd->nd_xprint_ttdriver->name = nd->nd_xprint_name; - nd->nd_xprint_ttdriver->name_base = 0; - nd->nd_xprint_ttdriver->major = nd->nd_serial_ttdriver->major; - nd->nd_xprint_ttdriver->minor_start = 0x80; - nd->nd_xprint_ttdriver->type = TTY_DRIVER_TYPE_SERIAL; - nd->nd_xprint_ttdriver->subtype = SERIAL_TYPE_XPRINT; - nd->nd_xprint_ttdriver->init_termios = DefaultTermios; - nd->nd_xprint_ttdriver->driver_name = "dgrp"; - nd->nd_xprint_ttdriver->flags = (TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | - TTY_DRIVER_HARDWARE_BREAK); - - /* The kernel wants space to store pointers to tty_structs. */ - nd->nd_xprint_ttdriver->ttys = - kzalloc(CHAN_MAX * sizeof(struct tty_struct *), GFP_KERNEL); - if (!nd->nd_xprint_ttdriver->ttys) - return -ENOMEM; - - tty_set_operations(nd->nd_xprint_ttdriver, &dgrp_tty_ops); - - if (dgrp_register_prdevices) { - if (!(nd->nd_ttdriver_flags & XPRINT_TTDRV_REG)) { - /* - * Register transparent print devices - */ - rc = tty_register_driver(nd->nd_xprint_ttdriver); - if (rc < 0) - return rc; - nd->nd_ttdriver_flags |= XPRINT_TTDRV_REG; - } - } - - for (i = 0; i < CHAN_MAX; i++) { - struct ch_struct *ch = nd->nd_chan + i; - - ch->ch_nd = nd; - ch->ch_digi = digi_init; - ch->ch_edelay = 100; - ch->ch_custom_speed = 0; - ch->ch_portnum = i; - ch->ch_tun.un_ch = ch; - ch->ch_pun.un_ch = ch; - ch->ch_tun.un_type = SERIAL_TYPE_NORMAL; - ch->ch_pun.un_type = SERIAL_TYPE_XPRINT; - - init_waitqueue_head(&(ch->ch_flag_wait)); - init_waitqueue_head(&(ch->ch_sleep)); - - init_waitqueue_head(&(ch->ch_tun.un_open_wait)); - init_waitqueue_head(&(ch->ch_tun.un_close_wait)); - - init_waitqueue_head(&(ch->ch_pun.un_open_wait)); - init_waitqueue_head(&(ch->ch_pun.un_close_wait)); - tty_port_init(&ch->port); - } - return 0; -} |