| /* |
| * |
| * 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 */ |
| |
| 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; |
| } |