/* * * Copyright 1999 Digi International (www.digi.com) * Gene Olson * James Puzzo * Jeff Randall * Scott Kilau * * 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 #include #include #include #include #include #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); tty_ldisc_flush(tty); 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 */ 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; }