diff options
Diffstat (limited to 'drivers/staging/dgrp/dgrp_net_ops.c')
-rw-r--r-- | drivers/staging/dgrp/dgrp_net_ops.c | 3666 |
1 files changed, 0 insertions, 3666 deletions
diff --git a/drivers/staging/dgrp/dgrp_net_ops.c b/drivers/staging/dgrp/dgrp_net_ops.c deleted file mode 100644 index 33ac7fb88cbd..000000000000 --- a/drivers/staging/dgrp/dgrp_net_ops.c +++ /dev/null @@ -1,3666 +0,0 @@ -/* - * - * Copyright 1999 Digi International (www.digi.com) - * James Puzzo <jamesp 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_net_ops.c - * - * Description: - * - * Handle the file operations required for the "network" devices. - * Includes those functions required to register the "net" devices - * in "/proc". - * - * Author: - * - * James A. Puzzo - * - */ - -#include <linux/module.h> -#include <linux/proc_fs.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/device.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/spinlock.h> -#include <linux/poll.h> -#include <linux/sched.h> -#include <linux/ratelimit.h> -#include <asm/unaligned.h> - -#define MYFLIPLEN TBUF_MAX - -#include "dgrp_common.h" - -#define TTY_FLIPBUF_SIZE 512 -#define DEVICE_NAME_SIZE 50 - -/* - * Generic helper function declarations - */ -static void parity_scan(struct ch_struct *ch, unsigned char *cbuf, - unsigned char *fbuf, int *len); - -/* - * File operation declarations - */ -static int dgrp_net_open(struct inode *, struct file *); -static int dgrp_net_release(struct inode *, struct file *); -static ssize_t dgrp_net_read(struct file *, char __user *, size_t, loff_t *); -static ssize_t dgrp_net_write(struct file *, const char __user *, size_t, - loff_t *); -static long dgrp_net_ioctl(struct file *file, unsigned int cmd, - unsigned long arg); -static unsigned int dgrp_net_select(struct file *file, - struct poll_table_struct *table); - -const struct file_operations dgrp_net_ops = { - .owner = THIS_MODULE, - .read = dgrp_net_read, - .write = dgrp_net_write, - .poll = dgrp_net_select, - .unlocked_ioctl = dgrp_net_ioctl, - .open = dgrp_net_open, - .release = dgrp_net_release, -}; - -/** - * dgrp_dump() -- prints memory for debugging purposes. - * @mem: Memory location which should be printed to the console - * @len: Number of bytes to be dumped - */ -static void dgrp_dump(u8 *mem, int len) -{ - int i; - - pr_debug("dgrp dump length = %d, data = ", len); - for (i = 0; i < len; ++i) - pr_debug("%.2x ", mem[i]); - pr_debug("\n"); -} - -/** - * dgrp_read_data_block() -- Read a data block - * @ch: struct ch_struct * - * @flipbuf: u8 * - * @flipbuf_size: size of flipbuf - */ -static void dgrp_read_data_block(struct ch_struct *ch, u8 *flipbuf, - int flipbuf_size) -{ - int t; - int n; - - if (flipbuf_size <= 0) - return; - - t = RBUF_MAX - ch->ch_rout; - n = flipbuf_size; - - if (n >= t) { - memcpy(flipbuf, ch->ch_rbuf + ch->ch_rout, t); - flipbuf += t; - n -= t; - ch->ch_rout = 0; - } - - memcpy(flipbuf, ch->ch_rbuf + ch->ch_rout, n); - flipbuf += n; - ch->ch_rout += n; -} - - -/** - * dgrp_input() -- send data to the line disipline - * @ch: pointer to channel struct - * - * Copys the rbuf to the flipbuf and sends to line discipline. - * Sends input buffer data to the line discipline. - * - */ -static void dgrp_input(struct ch_struct *ch) -{ - struct nd_struct *nd; - struct tty_struct *tty; - int data_len; - int len; - int tty_count; - ulong lock_flags; - u8 *myflipbuf; - u8 *myflipflagbuf; - - if (!ch) - return; - - nd = ch->ch_nd; - - if (!nd) - return; - - spin_lock_irqsave(&nd->nd_lock, lock_flags); - - myflipbuf = nd->nd_inputbuf; - myflipflagbuf = nd->nd_inputflagbuf; - - if (!ch->ch_open_count) { - ch->ch_rout = ch->ch_rin; - goto out; - } - - if (ch->ch_tun.un_flag & UN_CLOSING) { - ch->ch_rout = ch->ch_rin; - goto out; - } - - tty = (ch->ch_tun).un_tty; - - - if (!tty || tty->magic != TTY_MAGIC) { - ch->ch_rout = ch->ch_rin; - goto out; - } - - tty_count = tty->count; - if (!tty_count) { - ch->ch_rout = ch->ch_rin; - goto out; - } - - if (tty->closing || test_bit(TTY_CLOSING, &tty->flags)) { - ch->ch_rout = ch->ch_rin; - goto out; - } - - spin_unlock_irqrestore(&nd->nd_lock, lock_flags); - - /* data_len should be the number of chars that we read in */ - data_len = (ch->ch_rin - ch->ch_rout) & RBUF_MASK; - - /* len is the amount of data we are going to transfer here */ - len = tty_buffer_request_room(&ch->port, data_len); - - /* Check DPA flow control */ - if ((nd->nd_dpa_debug) && - (nd->nd_dpa_flag & DPA_WAIT_SPACE) && - (nd->nd_dpa_port == MINOR(tty_devnum(ch->ch_tun.un_tty)))) - len = 0; - - if ((len) && !(ch->ch_flag & CH_RXSTOP)) { - - dgrp_read_data_block(ch, myflipbuf, len); - - if (I_PARMRK(tty) || I_BRKINT(tty) || I_INPCK(tty)) - parity_scan(ch, myflipbuf, myflipflagbuf, &len); - else - memset(myflipflagbuf, TTY_NORMAL, len); - - if ((nd->nd_dpa_debug) && - (nd->nd_dpa_port == PORT_NUM(MINOR(tty_devnum(tty))))) - dgrp_dpa_data(nd, 1, myflipbuf, len); - - tty_insert_flip_string_flags(&ch->port, myflipbuf, - myflipflagbuf, len); - tty_flip_buffer_push(&ch->port); - - ch->ch_rxcount += len; - } - - /* - * Wake up any sleepers (maybe dgrp close) that might be waiting - * for a channel flag state change. - */ - wake_up_interruptible(&ch->ch_flag_wait); - return; - -out: - spin_unlock_irqrestore(&nd->nd_lock, lock_flags); -} - - -/* - * parity_scan - * - * Loop to inspect each single character or 0xFF escape. - * - * if PARMRK & ~DOSMODE: - * 0xFF 0xFF Normal 0xFF character, escaped - * to eliminate confusion. - * 0xFF 0x00 0x00 Break - * 0xFF 0x00 CC Error character CC. - * CC Normal character CC. - * - * if PARMRK & DOSMODE: - * 0xFF 0x18 0x00 Break - * 0xFF 0x08 0x00 Framing Error - * 0xFF 0x04 0x00 Parity error - * 0xFF 0x0C 0x00 Both Framing and Parity error - * - * TODO: do we need to do the XMODEM, XOFF, XON, XANY processing?? - * as per protocol - */ -static void parity_scan(struct ch_struct *ch, unsigned char *cbuf, - unsigned char *fbuf, int *len) -{ - int l = *len; - int count = 0; - int DOS = ((ch->ch_iflag & IF_DOSMODE) == 0 ? 0 : 1); - unsigned char *cout; /* character buffer */ - unsigned char *fout; /* flag buffer */ - unsigned char *in; - unsigned char c; - - in = cbuf; - cout = cbuf; - fout = fbuf; - - while (l--) { - c = *in; - in++; - - switch (ch->ch_pscan_state) { - default: - /* reset to sanity and fall through */ - ch->ch_pscan_state = 0; - - case 0: - /* No FF seen yet */ - if (c == 0xff) /* delete this character from stream */ - ch->ch_pscan_state = 1; - else { - *cout++ = c; - *fout++ = TTY_NORMAL; - count += 1; - } - break; - - case 1: - /* first FF seen */ - if (c == 0xff) { - /* doubled ff, transform to single ff */ - *cout++ = c; - *fout++ = TTY_NORMAL; - count += 1; - ch->ch_pscan_state = 0; - } else { - /* save value examination in next state */ - ch->ch_pscan_savechar = c; - ch->ch_pscan_state = 2; - } - break; - - case 2: - /* third character of ff sequence */ - *cout++ = c; - if (DOS) { - if (ch->ch_pscan_savechar & 0x10) - *fout++ = TTY_BREAK; - else if (ch->ch_pscan_savechar & 0x08) - *fout++ = TTY_FRAME; - else - /* - * either marked as a parity error, - * indeterminate, or not in DOSMODE - * call it a parity error - */ - *fout++ = TTY_PARITY; - } else { - /* case FF XX ?? where XX is not 00 */ - if (ch->ch_pscan_savechar & 0xff) { - /* this should not happen */ - pr_info("%s: parity_scan: error unexpected byte\n", - __func__); - *fout++ = TTY_PARITY; - } - /* case FF 00 XX where XX is not 00 */ - else if (c == 0xff) - *fout++ = TTY_PARITY; - /* case FF 00 00 */ - else - *fout++ = TTY_BREAK; - - } - count += 1; - ch->ch_pscan_state = 0; - } - } - *len = count; -} - - -/** - * dgrp_net_idle() -- Idle the network connection - * @nd: pointer to node structure to idle - */ -static void dgrp_net_idle(struct nd_struct *nd) -{ - struct ch_struct *ch; - int i; - - nd->nd_tx_work = 1; - - nd->nd_state = NS_IDLE; - nd->nd_flag = 0; - - for (i = nd->nd_seq_out; ; i = (i + 1) & SEQ_MASK) { - if (!nd->nd_seq_wait[i]) { - nd->nd_seq_wait[i] = 0; - wake_up_interruptible(&nd->nd_seq_wque[i]); - } - - if (i == nd->nd_seq_in) - break; - } - - nd->nd_seq_out = nd->nd_seq_in; - - nd->nd_unack = 0; - nd->nd_remain = 0; - - nd->nd_tx_module = 0x10; - nd->nd_rx_module = 0x00; - - for (i = 0, ch = nd->nd_chan; i < CHAN_MAX; i++, ch++) { - ch->ch_state = CS_IDLE; - - ch->ch_otype = 0; - ch->ch_otype_waiting = 0; - } -} - -/* - * Increase the number of channels, waking up any - * threads that might be waiting for the channels - * to appear. - */ -static void increase_channel_count(struct nd_struct *nd, int n) -{ - struct ch_struct *ch; - struct device *classp; - char name[DEVICE_NAME_SIZE]; - int ret; - u8 *buf; - int i; - - for (i = nd->nd_chan_count; i < n; ++i) { - ch = nd->nd_chan + i; - - /* FIXME: return a useful error instead! */ - buf = kmalloc(TBUF_MAX, GFP_KERNEL); - if (!buf) - return; - - if (ch->ch_tbuf) - pr_info_ratelimited("%s - ch_tbuf was not NULL\n", - __func__); - - ch->ch_tbuf = buf; - - buf = kmalloc(RBUF_MAX, GFP_KERNEL); - if (!buf) - return; - - if (ch->ch_rbuf) - pr_info("%s - ch_rbuf was not NULL\n", - __func__); - ch->ch_rbuf = buf; - - classp = tty_port_register_device(&ch->port, - nd->nd_serial_ttdriver, i, - NULL); - - ch->ch_tun.un_sysfs = classp; - snprintf(name, DEVICE_NAME_SIZE, "tty_%d", i); - - dgrp_create_tty_sysfs(&ch->ch_tun, classp); - ret = sysfs_create_link(&nd->nd_class_dev->kobj, - &classp->kobj, name); - - /* NOTE: We don't support "cu" devices anymore, - * so you will notice we don't register them - * here anymore. */ - if (dgrp_register_prdevices) { - classp = tty_register_device(nd->nd_xprint_ttdriver, - i, NULL); - ch->ch_pun.un_sysfs = classp; - snprintf(name, DEVICE_NAME_SIZE, "pr_%d", i); - - dgrp_create_tty_sysfs(&ch->ch_pun, classp); - ret = sysfs_create_link(&nd->nd_class_dev->kobj, - &classp->kobj, name); - } - - nd->nd_chan_count = i + 1; - wake_up_interruptible(&ch->ch_flag_wait); - } -} - -/* - * Decrease the number of channels, and wake up any threads that might - * be waiting on the channels that vanished. - */ -static void decrease_channel_count(struct nd_struct *nd, int n) -{ - struct ch_struct *ch; - char name[DEVICE_NAME_SIZE]; - int i; - - for (i = nd->nd_chan_count - 1; i >= n; --i) { - ch = nd->nd_chan + i; - - /* - * Make any open ports inoperative. - */ - ch->ch_state = CS_IDLE; - - ch->ch_otype = 0; - ch->ch_otype_waiting = 0; - - /* - * Only "HANGUP" if we care about carrier - * transitions and we are already open. - */ - if (ch->ch_open_count != 0) { - ch->ch_flag |= CH_HANGUP; - dgrp_carrier(ch); - } - - /* - * Unlike the CH_HANGUP flag above, use another - * flag to indicate to the RealPort state machine - * that this port has disappeared. - */ - if (ch->ch_open_count != 0) - ch->ch_flag |= CH_PORT_GONE; - - wake_up_interruptible(&ch->ch_flag_wait); - - nd->nd_chan_count = i; - - kfree(ch->ch_tbuf); - ch->ch_tbuf = NULL; - - kfree(ch->ch_rbuf); - ch->ch_rbuf = NULL; - - nd->nd_chan_count = i; - - dgrp_remove_tty_sysfs(ch->ch_tun.un_sysfs); - snprintf(name, DEVICE_NAME_SIZE, "tty_%d", i); - sysfs_remove_link(&nd->nd_class_dev->kobj, name); - tty_unregister_device(nd->nd_serial_ttdriver, i); - - /* - * NOTE: We don't support "cu" devices anymore, so don't - * unregister them here anymore. - */ - - if (dgrp_register_prdevices) { - dgrp_remove_tty_sysfs(ch->ch_pun.un_sysfs); - snprintf(name, DEVICE_NAME_SIZE, "pr_%d", i); - sysfs_remove_link(&nd->nd_class_dev->kobj, name); - tty_unregister_device(nd->nd_xprint_ttdriver, i); - } - } -} - -/** - * dgrp_chan_count() -- Adjust the node channel count. - * @nd: pointer to a node structure - * @n: new value for channel count - * - * Adjusts the node channel count. If new ports have appeared, it tries - * to signal those processes that might have been waiting for ports to - * appear. If ports have disappeared it tries to signal those processes - * that might be hung waiting for a response for the now non-existant port. - */ -static void dgrp_chan_count(struct nd_struct *nd, int n) -{ - if (n == nd->nd_chan_count) - return; - - if (n > nd->nd_chan_count) - increase_channel_count(nd, n); - - if (n < nd->nd_chan_count) - decrease_channel_count(nd, n); -} - -/** - * dgrp_monitor() -- send data to the device monitor queue - * @nd: pointer to a node structure - * @buf: data to copy to the monitoring buffer - * @len: number of bytes to transfer to the buffer - * - * Called by the net device routines to send data to the device - * monitor queue. If the device monitor buffer is too full to - * accept the data, it waits until the buffer is ready. - */ -static void dgrp_monitor(struct nd_struct *nd, u8 *buf, int len) -{ - int n; - int r; - int rtn; - - /* - * Grab monitor lock. - */ - down(&nd->nd_mon_semaphore); - - /* - * Loop while data remains. - */ - while ((len > 0) && (nd->nd_mon_buf)) { - /* - * Determine the amount of available space left in the - * buffer. If there's none, wait until some appears. - */ - - n = (nd->nd_mon_out - nd->nd_mon_in - 1) & MON_MASK; - - if (!n) { - nd->nd_mon_flag |= MON_WAIT_SPACE; - - up(&nd->nd_mon_semaphore); - - /* - * Go to sleep waiting until the condition becomes true. - */ - rtn = wait_event_interruptible(nd->nd_mon_wqueue, - ((nd->nd_mon_flag & MON_WAIT_SPACE) == 0)); - -/* FIXME: really ignore rtn? */ - - /* - * We can't exit here if we receive a signal, since - * to do so would trash the debug stream. - */ - - down(&nd->nd_mon_semaphore); - - continue; - } - - /* - * Copy as much data as will fit. - */ - - if (n > len) - n = len; - - r = MON_MAX - nd->nd_mon_in; - - if (r <= n) { - memcpy(nd->nd_mon_buf + nd->nd_mon_in, buf, r); - - n -= r; - - nd->nd_mon_in = 0; - - buf += r; - len -= r; - } - - memcpy(nd->nd_mon_buf + nd->nd_mon_in, buf, n); - - nd->nd_mon_in += n; - - buf += n; - len -= n; - - if (nd->nd_mon_in >= MON_MAX) - pr_info_ratelimited("%s - nd_mon_in (%i) >= MON_MAX\n", - __func__, nd->nd_mon_in); - - /* - * Wakeup any thread waiting for data - */ - - if (nd->nd_mon_flag & MON_WAIT_DATA) { - nd->nd_mon_flag &= ~MON_WAIT_DATA; - wake_up_interruptible(&nd->nd_mon_wqueue); - } - } - - /* - * Release the monitor lock. - */ - up(&nd->nd_mon_semaphore); -} - -/** - * dgrp_encode_time() -- Encodes rpdump time into a 4-byte quantity. - * @nd: pointer to a node structure - * @buf: destination buffer - * - * Encodes "rpdump" time into a 4-byte quantity. Time is measured since - * open. - */ -static void dgrp_encode_time(struct nd_struct *nd, u8 *buf) -{ - ulong t; - - /* - * Convert time in HZ since open to time in milliseconds - * since open. - */ - t = jiffies - nd->nd_mon_lbolt; - t = 1000 * (t / HZ) + 1000 * (t % HZ) / HZ; - - put_unaligned_be32((uint)(t & 0xffffffff), buf); -} - - - -/** - * dgrp_monitor_message() -- Builds a rpdump style message. - * @nd: pointer to a node structure - * @message: destination buffer - */ -static void dgrp_monitor_message(struct nd_struct *nd, char *message) -{ - u8 header[7]; - int n; - - header[0] = RPDUMP_MESSAGE; - - dgrp_encode_time(nd, header + 1); - - n = strlen(message); - - put_unaligned_be16(n, header + 5); - - dgrp_monitor(nd, header, sizeof(header)); - dgrp_monitor(nd, (u8 *) message, n); -} - - - -/** - * dgrp_monitor_reset() -- Note a reset in the monitoring buffer. - * @nd: pointer to a node structure - */ -static void dgrp_monitor_reset(struct nd_struct *nd) -{ - u8 header[5]; - - header[0] = RPDUMP_RESET; - - dgrp_encode_time(nd, header + 1); - - dgrp_monitor(nd, header, sizeof(header)); -} - -/** - * dgrp_monitor_data() -- builds a monitor data packet - * @nd: pointer to a node structure - * @type: type of message to be logged - * @buf: data to be logged - * @size: number of bytes in the buffer - */ -static void dgrp_monitor_data(struct nd_struct *nd, u8 type, u8 *buf, int size) -{ - u8 header[7]; - - header[0] = type; - - dgrp_encode_time(nd, header + 1); - - put_unaligned_be16(size, header + 5); - - dgrp_monitor(nd, header, sizeof(header)); - dgrp_monitor(nd, buf, size); -} - -static int alloc_nd_buffers(struct nd_struct *nd) -{ - - nd->nd_iobuf = NULL; - nd->nd_writebuf = NULL; - nd->nd_inputbuf = NULL; - nd->nd_inputflagbuf = NULL; - - /* - * Allocate the network read/write buffer. - */ - nd->nd_iobuf = kzalloc(UIO_MAX + 10, GFP_KERNEL); - if (!nd->nd_iobuf) - goto out_err; - - /* - * Allocate a buffer for doing the copy from user space to - * kernel space in the write routines. - */ - nd->nd_writebuf = kzalloc(WRITEBUFLEN, GFP_KERNEL); - if (!nd->nd_writebuf) - goto out_err; - - /* - * Allocate a buffer for doing the copy from kernel space to - * tty buffer space in the read routines. - */ - nd->nd_inputbuf = kzalloc(MYFLIPLEN, GFP_KERNEL); - if (!nd->nd_inputbuf) - goto out_err; - - /* - * Allocate a buffer for doing the copy from kernel space to - * tty buffer space in the read routines. - */ - nd->nd_inputflagbuf = kzalloc(MYFLIPLEN, GFP_KERNEL); - if (!nd->nd_inputflagbuf) - goto out_err; - - return 0; - -out_err: - kfree(nd->nd_iobuf); - kfree(nd->nd_writebuf); - kfree(nd->nd_inputbuf); - kfree(nd->nd_inputflagbuf); - return -ENOMEM; -} - -/* - * dgrp_net_open() -- Open the NET device for a particular PortServer - */ -static int dgrp_net_open(struct inode *inode, struct file *file) -{ - struct nd_struct *nd; - ulong lock_flags; - int rtn; - - rtn = try_module_get(THIS_MODULE); - if (!rtn) - return -EAGAIN; - - if (!capable(CAP_SYS_ADMIN)) { - rtn = -EPERM; - goto done; - } - - /* - * Make sure that the "private_data" field hasn't already been used. - */ - if (file->private_data) { - rtn = -EINVAL; - goto done; - } - - /* - * Get the node pointer, and fail if it doesn't exist. - */ - nd = PDE_DATA(inode); - if (!nd) { - rtn = -ENXIO; - goto done; - } - - file->private_data = (void *) nd; - - /* - * Grab the NET lock. - */ - down(&nd->nd_net_semaphore); - - if (nd->nd_state != NS_CLOSED) { - rtn = -EBUSY; - goto unlock; - } - - /* - * Initialize the link speed parameters. - */ - - nd->nd_link.lk_fast_rate = UIO_MAX; - nd->nd_link.lk_slow_rate = UIO_MAX; - - nd->nd_link.lk_fast_delay = 1000; - nd->nd_link.lk_slow_delay = 1000; - - nd->nd_link.lk_header_size = 46; - - - rtn = alloc_nd_buffers(nd); - if (rtn) - goto unlock; - - /* - * The port is now open, so move it to the IDLE state - */ - dgrp_net_idle(nd); - - nd->nd_tx_time = jiffies; - - /* - * If the polling routing is not running, start it running here - */ - spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags); - - if (!dgrp_poll_data.node_active_count) { - dgrp_poll_data.node_active_count = 2; - dgrp_poll_data.timer.expires = jiffies + - dgrp_poll_tick * HZ / 1000; - add_timer(&dgrp_poll_data.timer); - } - - spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags); - - dgrp_monitor_message(nd, "Net Open"); - -unlock: - /* - * Release the NET lock. - */ - up(&nd->nd_net_semaphore); - -done: - if (rtn) - module_put(THIS_MODULE); - - return rtn; -} - -/* dgrp_net_release() -- close the NET device for a particular PortServer */ -static int dgrp_net_release(struct inode *inode, struct file *file) -{ - struct nd_struct *nd; - ulong lock_flags; - - nd = (struct nd_struct *)(file->private_data); - if (!nd) - goto done; - -/* TODO : historical locking placeholder */ -/* - * In the HPUX version of the RealPort driver (which served as a basis - * for this driver) this locking code was used. Saved if ever we need - * to review the locking under Linux. - */ -/* spinlock(&nd->nd_lock); */ - - - /* - * Grab the NET lock. - */ - down(&nd->nd_net_semaphore); - - /* - * Before "closing" the internal connection, make sure all - * ports are "idle". - */ - dgrp_net_idle(nd); - - nd->nd_state = NS_CLOSED; - nd->nd_flag = 0; - - /* - * TODO ... must the wait queue be reset on close? - * should any pending waiters be reset? - * Let's decide to assert that the waitq is empty... and see - * how soon we break. - */ - if (waitqueue_active(&nd->nd_tx_waitq)) - pr_info("%s - expected waitqueue_active to be false\n", - __func__); - - nd->nd_send = 0; - - kfree(nd->nd_iobuf); - nd->nd_iobuf = NULL; - -/* TODO : historical locking placeholder */ -/* - * In the HPUX version of the RealPort driver (which served as a basis - * for this driver) this locking code was used. Saved if ever we need - * to review the locking under Linux. - */ -/* spinunlock( &nd->nd_lock ); */ - - - kfree(nd->nd_writebuf); - nd->nd_writebuf = NULL; - - kfree(nd->nd_inputbuf); - nd->nd_inputbuf = NULL; - - kfree(nd->nd_inputflagbuf); - nd->nd_inputflagbuf = NULL; - -/* TODO : historical locking placeholder */ -/* - * In the HPUX version of the RealPort driver (which served as a basis - * for this driver) this locking code was used. Saved if ever we need - * to review the locking under Linux. - */ -/* spinlock(&nd->nd_lock); */ - - /* - * Set the active port count to zero. - */ - dgrp_chan_count(nd, 0); - -/* TODO : historical locking placeholder */ -/* - * In the HPUX version of the RealPort driver (which served as a basis - * for this driver) this locking code was used. Saved if ever we need - * to review the locking under Linux. - */ -/* spinunlock(&nd->nd_lock); */ - - /* - * Release the NET lock. - */ - up(&nd->nd_net_semaphore); - - /* - * Cause the poller to stop scheduling itself if this is - * the last active node. - */ - spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags); - - if (dgrp_poll_data.node_active_count == 2) { - del_timer(&dgrp_poll_data.timer); - dgrp_poll_data.node_active_count = 0; - } - - spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags); - - down(&nd->nd_net_semaphore); - - dgrp_monitor_message(nd, "Net Close"); - - up(&nd->nd_net_semaphore); - -done: - module_put(THIS_MODULE); - file->private_data = NULL; - return 0; -} - -/* used in dgrp_send to setup command header */ -static inline u8 *set_cmd_header(u8 *b, u8 port, u8 cmd) -{ - *b++ = 0xb0 + (port & 0x0f); - *b++ = cmd; - return b; -} - -/** - * dgrp_send() -- build a packet for transmission to the server - * @nd: pointer to a node structure - * @tmax: maximum bytes to transmit - * - * returns number of bytes sent - */ -static int dgrp_send(struct nd_struct *nd, long tmax) -{ - struct ch_struct *ch = nd->nd_chan; - u8 *b; - u8 *buf; - u8 *mbuf; - u8 port; - int mod; - long send; - int maxport; - long lastport = -1; - ushort rwin; - long in; - ushort n; - long t; - long ttotal; - long tchan; - long tsend; - ushort tsafe; - long work; - long send_sync; - long wanted_sync_port = -1; - ushort tdata[CHAN_MAX]; - long used_buffer; - - mbuf = nd->nd_iobuf + UIO_BASE; - buf = b = mbuf; - - send_sync = nd->nd_link.lk_slow_rate < UIO_MAX; - - ttotal = 0; - tchan = 0; - - memset(tdata, 0, sizeof(tdata)); - - - /* - * If there are any outstanding requests to be serviced, - * service them here. - */ - if (nd->nd_send & NR_PASSWORD) { - - /* - * Send Password response. - */ - - b[0] = 0xfc; - b[1] = 0x20; - put_unaligned_be16(strlen(nd->password), b + 2); - b += 4; - b += strlen(nd->password); - nd->nd_send &= ~(NR_PASSWORD); - } - - - /* - * Loop over all modules to generate commands, and determine - * the amount of data queued for transmit. - */ - - for (mod = 0, port = 0; port < nd->nd_chan_count; mod++) { - /* - * If this is not the current module, enter a module select - * code in the buffer. - */ - - if (mod != nd->nd_tx_module) - mbuf = ++b; - - /* - * Loop to process one module. - */ - - maxport = port + 16; - - if (maxport > nd->nd_chan_count) - maxport = nd->nd_chan_count; - - for (; port < maxport; port++, ch++) { - /* - * Switch based on channel state. - */ - - switch (ch->ch_state) { - /* - * Send requests when the port is closed, and there - * are no Open, Close or Cancel requests expected. - */ - - case CS_IDLE: - /* - * Wait until any open error code - * has been delivered to all - * associated ports. - */ - - if (ch->ch_open_error) { - if (ch->ch_wait_count[ch->ch_otype]) { - work = 1; - break; - } - - ch->ch_open_error = 0; - } - - /* - * Wait until the channel HANGUP flag is reset - * before sending the first open. We can only - * get to this state after a server disconnect. - */ - - if ((ch->ch_flag & CH_HANGUP) != 0) - break; - - /* - * If recovering from a TCP disconnect, or if - * there is an immediate open pending, send an - * Immediate Open request. - */ - if ((ch->ch_flag & CH_PORT_GONE) || - ch->ch_wait_count[OTYPE_IMMEDIATE] != 0) { - b = set_cmd_header(b, port, 10); - *b++ = 0; - - ch->ch_state = CS_WAIT_OPEN; - ch->ch_otype = OTYPE_IMMEDIATE; - break; - } - - /* - * If there is no Persistent or Incoming Open on the wait - * list in the server, and a thread is waiting for a - * Persistent or Incoming Open, send a Persistent or Incoming - * Open Request. - */ - if (ch->ch_otype_waiting == 0) { - if (ch->ch_wait_count[OTYPE_PERSISTENT] != 0) { - b = set_cmd_header(b, port, 10); - *b++ = 1; - - ch->ch_state = CS_WAIT_OPEN; - ch->ch_otype = OTYPE_PERSISTENT; - } else if (ch->ch_wait_count[OTYPE_INCOMING] != 0) { - b = set_cmd_header(b, port, 10); - *b++ = 2; - - ch->ch_state = CS_WAIT_OPEN; - ch->ch_otype = OTYPE_INCOMING; - } - break; - } - - /* - * If a Persistent or Incoming Open is pending in - * the server, but there is no longer an open - * thread waiting for it, cancel the request. - */ - - if (ch->ch_wait_count[ch->ch_otype_waiting] == 0) { - b = set_cmd_header(b, port, 10); - *b++ = 4; - - ch->ch_state = CS_WAIT_CANCEL; - ch->ch_otype = ch->ch_otype_waiting; - } - break; - - /* - * Send port parameter queries. - */ - case CS_SEND_QUERY: - /* - * Clear out all FEP state that might remain - * from the last connection. - */ - - ch->ch_flag |= CH_PARAM; - - ch->ch_flag &= ~CH_RX_FLUSH; - - ch->ch_expect = 0; - - ch->ch_s_tin = 0; - ch->ch_s_tpos = 0; - ch->ch_s_tsize = 0; - ch->ch_s_treq = 0; - ch->ch_s_elast = 0; - - ch->ch_s_rin = 0; - ch->ch_s_rwin = 0; - ch->ch_s_rsize = 0; - - ch->ch_s_tmax = 0; - ch->ch_s_ttime = 0; - ch->ch_s_rmax = 0; - ch->ch_s_rtime = 0; - ch->ch_s_rlow = 0; - ch->ch_s_rhigh = 0; - - ch->ch_s_brate = 0; - ch->ch_s_iflag = 0; - ch->ch_s_cflag = 0; - ch->ch_s_oflag = 0; - ch->ch_s_xflag = 0; - - ch->ch_s_mout = 0; - ch->ch_s_mflow = 0; - ch->ch_s_mctrl = 0; - ch->ch_s_xon = 0; - ch->ch_s_xoff = 0; - ch->ch_s_lnext = 0; - ch->ch_s_xxon = 0; - ch->ch_s_xxoff = 0; - - /* Send Sequence Request */ - b = set_cmd_header(b, port, 14); - - /* Configure Event Conditions Packet */ - b = set_cmd_header(b, port, 42); - put_unaligned_be16(0x02c0, b); - b += 2; - *b++ = (DM_DTR | DM_RTS | DM_CTS | - DM_DSR | DM_RI | DM_CD); - - /* Send Status Request */ - b = set_cmd_header(b, port, 16); - - /* Send Buffer Request */ - b = set_cmd_header(b, port, 20); - - /* Send Port Capability Request */ - b = set_cmd_header(b, port, 22); - - ch->ch_expect = (RR_SEQUENCE | - RR_STATUS | - RR_BUFFER | - RR_CAPABILITY); - - ch->ch_state = CS_WAIT_QUERY; - - /* Raise modem signals */ - b = set_cmd_header(b, port, 44); - - if (ch->ch_flag & CH_PORT_GONE) - ch->ch_s_mout = ch->ch_mout; - else - ch->ch_s_mout = ch->ch_mout = DM_DTR | DM_RTS; - - *b++ = ch->ch_mout; - *b++ = ch->ch_s_mflow = 0; - *b++ = ch->ch_s_mctrl = ch->ch_mctrl = 0; - - if (ch->ch_flag & CH_PORT_GONE) - ch->ch_flag &= ~CH_PORT_GONE; - - break; - - /* - * Handle normal open and ready mode. - */ - - case CS_READY: - - /* - * If the port is not open, and there are no - * no longer any ports requesting an open, - * then close the port. - */ - - if (ch->ch_open_count == 0 && - ch->ch_wait_count[ch->ch_otype] == 0) { - goto send_close; - } - - /* - * Process waiting input. - * - * If there is no one to read it, discard the data. - * - * Otherwise if we are not in fastcook mode, or if there is a - * fastcook thread waiting for data, send the data to the - * line discipline. - */ - if (ch->ch_rin != ch->ch_rout) { - if (ch->ch_tun.un_open_count == 0 || - (ch->ch_tun.un_flag & UN_CLOSING) || - (ch->ch_cflag & CF_CREAD) == 0) { - ch->ch_rout = ch->ch_rin; - } else if ((ch->ch_flag & CH_FAST_READ) == 0 || - ch->ch_inwait != 0) { - dgrp_input(ch); - - if (ch->ch_rin != ch->ch_rout) - work = 1; - } - } - - /* - * Handle receive flush, and changes to - * server port parameters. - */ - - if (ch->ch_flag & (CH_RX_FLUSH | CH_PARAM)) { - /* - * If we are in receive flush mode, - * and enough data has gone by, reset - * receive flush mode. - */ - if (ch->ch_flag & CH_RX_FLUSH) { - if (((ch->ch_flush_seq - nd->nd_seq_out) & SEQ_MASK) > - ((nd->nd_seq_in - nd->nd_seq_out) & SEQ_MASK)) - ch->ch_flag &= ~CH_RX_FLUSH; - else - work = 1; - } - - /* - * Send TMAX, TTIME. - */ - - if (ch->ch_s_tmax != ch->ch_tmax || - ch->ch_s_ttime != ch->ch_ttime) { - b = set_cmd_header(b, port, 48); - - ch->ch_s_tmax = ch->ch_tmax; - ch->ch_s_ttime = ch->ch_ttime; - - put_unaligned_be16(ch->ch_s_tmax, - b); - b += 2; - - put_unaligned_be16(ch->ch_s_ttime, - b); - b += 2; - } - - /* - * Send RLOW, RHIGH. - */ - - if (ch->ch_s_rlow != ch->ch_rlow || - ch->ch_s_rhigh != ch->ch_rhigh) { - b = set_cmd_header(b, port, 45); - - ch->ch_s_rlow = ch->ch_rlow; - ch->ch_s_rhigh = ch->ch_rhigh; - - put_unaligned_be16(ch->ch_s_rlow, - b); - b += 2; - - put_unaligned_be16(ch->ch_s_rhigh, - b); - b += 2; - } - - /* - * Send BRATE, CFLAG, IFLAG, - * OFLAG, XFLAG. - */ - - if (ch->ch_s_brate != ch->ch_brate || - ch->ch_s_cflag != ch->ch_cflag || - ch->ch_s_iflag != ch->ch_iflag || - ch->ch_s_oflag != ch->ch_oflag || - ch->ch_s_xflag != ch->ch_xflag) { - b = set_cmd_header(b, port, 40); - - ch->ch_s_brate = ch->ch_brate; - ch->ch_s_cflag = ch->ch_cflag; - ch->ch_s_iflag = ch->ch_iflag; - ch->ch_s_oflag = ch->ch_oflag; - ch->ch_s_xflag = ch->ch_xflag; - - put_unaligned_be16(ch->ch_s_brate, - b); - b += 2; - - put_unaligned_be16(ch->ch_s_cflag, - b); - b += 2; - - put_unaligned_be16(ch->ch_s_iflag, - b); - b += 2; - - put_unaligned_be16(ch->ch_s_oflag, - b); - b += 2; - - put_unaligned_be16(ch->ch_s_xflag, - b); - b += 2; - } - - /* - * Send MOUT, MFLOW, MCTRL. - */ - - if (ch->ch_s_mout != ch->ch_mout || - ch->ch_s_mflow != ch->ch_mflow || - ch->ch_s_mctrl != ch->ch_mctrl) { - b = set_cmd_header(b, port, 44); - - *b++ = ch->ch_s_mout = ch->ch_mout; - *b++ = ch->ch_s_mflow = ch->ch_mflow; - *b++ = ch->ch_s_mctrl = ch->ch_mctrl; - } - - /* - * Send Flow control characters. - */ - - if (ch->ch_s_xon != ch->ch_xon || - ch->ch_s_xoff != ch->ch_xoff || - ch->ch_s_lnext != ch->ch_lnext || - ch->ch_s_xxon != ch->ch_xxon || - ch->ch_s_xxoff != ch->ch_xxoff) { - b = set_cmd_header(b, port, 46); - - *b++ = ch->ch_s_xon = ch->ch_xon; - *b++ = ch->ch_s_xoff = ch->ch_xoff; - *b++ = ch->ch_s_lnext = ch->ch_lnext; - *b++ = ch->ch_s_xxon = ch->ch_xxon; - *b++ = ch->ch_s_xxoff = ch->ch_xxoff; - } - - /* - * Send RMAX, RTIME. - */ - - if (ch->ch_s_rmax != ch->ch_rmax || - ch->ch_s_rtime != ch->ch_rtime) { - b = set_cmd_header(b, port, 47); - - ch->ch_s_rmax = ch->ch_rmax; - ch->ch_s_rtime = ch->ch_rtime; - - put_unaligned_be16(ch->ch_s_rmax, - b); - b += 2; - - put_unaligned_be16(ch->ch_s_rtime, - b); - b += 2; - } - - ch->ch_flag &= ~CH_PARAM; - wake_up_interruptible(&ch->ch_flag_wait); - } - - - /* - * Handle action commands. - */ - - if (ch->ch_send != 0) { - /* int send = ch->ch_send & ~ch->ch_expect; */ - send = ch->ch_send & ~ch->ch_expect; - - /* Send character immediate */ - if ((send & RR_TX_ICHAR) != 0) { - b = set_cmd_header(b, port, 60); - - *b++ = ch->ch_xon; - ch->ch_expect |= RR_TX_ICHAR; - } - - /* BREAK request */ - if ((send & RR_TX_BREAK) != 0) { - if (ch->ch_break_time != 0) { - b = set_cmd_header(b, port, 61); - put_unaligned_be16(ch->ch_break_time, - b); - b += 2; - - ch->ch_expect |= RR_TX_BREAK; - ch->ch_break_time = 0; - } else { - ch->ch_send &= ~RR_TX_BREAK; - ch->ch_flag &= ~CH_TX_BREAK; - wake_up_interruptible(&ch->ch_flag_wait); - } - } - - /* - * Flush input/output buffers. - */ - - if ((send & (RR_RX_FLUSH | RR_TX_FLUSH)) != 0) { - b = set_cmd_header(b, port, 62); - - *b++ = ((send & RR_TX_FLUSH) == 0 ? 1 : - (send & RR_RX_FLUSH) == 0 ? 2 : 3); - - if (send & RR_RX_FLUSH) { - ch->ch_flush_seq = nd->nd_seq_in; - ch->ch_flag |= CH_RX_FLUSH; - work = 1; - send_sync = 1; - wanted_sync_port = port; - } - - ch->ch_send &= ~(RR_RX_FLUSH | RR_TX_FLUSH); - } - - /* Pause input/output */ - if ((send & (RR_RX_STOP | RR_TX_STOP)) != 0) { - b = set_cmd_header(b, port, 63); - *b = 0; - - if ((send & RR_TX_STOP) != 0) - *b |= EV_OPU; - - if ((send & RR_RX_STOP) != 0) - *b |= EV_IPU; - - b++; - - ch->ch_send &= ~(RR_RX_STOP | RR_TX_STOP); - } - - /* Start input/output */ - if ((send & (RR_RX_START | RR_TX_START)) != 0) { - b = set_cmd_header(b, port, 64); - *b = 0; - - if ((send & RR_TX_START) != 0) - *b |= EV_OPU | EV_OPS | EV_OPX; - - if ((send & RR_RX_START) != 0) - *b |= EV_IPU | EV_IPS; - - b++; - - ch->ch_send &= ~(RR_RX_START | RR_TX_START); - } - } - - - /* - * Send a window sequence to acknowledge received data. - */ - - rwin = (ch->ch_s_rin + - ((ch->ch_rout - ch->ch_rin - 1) & RBUF_MASK)); - - n = (rwin - ch->ch_s_rwin) & 0xffff; - - if (n >= RBUF_MAX / 4) { - b[0] = 0xa0 + (port & 0xf); - ch->ch_s_rwin = rwin; - put_unaligned_be16(rwin, b + 1); - b += 3; - } - - /* - * If the terminal is waiting on LOW - * water or EMPTY, and the condition - * is now satisfied, call the line - * discipline to put more data in the - * buffer. - */ - - n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK; - - if ((ch->ch_tun.un_flag & (UN_EMPTY|UN_LOW)) != 0) { - if ((ch->ch_tun.un_flag & UN_LOW) != 0 ? - (n <= TBUF_LOW) : - (n == 0 && ch->ch_s_tpos == ch->ch_s_tin)) { - ch->ch_tun.un_flag &= ~(UN_EMPTY|UN_LOW); - - if (waitqueue_active(&((ch->ch_tun.un_tty)->write_wait))) - wake_up_interruptible(&((ch->ch_tun.un_tty)->write_wait)); - tty_wakeup(ch->ch_tun.un_tty); - n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK; - } - } - - /* - * If the printer is waiting on LOW - * water, TIME, EMPTY or PWAIT, and is - * now ready to put more data in the - * buffer, call the line discipline to - * do the job. - */ - - /* FIXME: jiffies - ch->ch_waketime can never - be < 0. Someone needs to work out what is - actually intended here */ - if (ch->ch_pun.un_open_count && - (ch->ch_pun.un_flag & - (UN_EMPTY|UN_TIME|UN_LOW|UN_PWAIT)) != 0) { - - if ((ch->ch_pun.un_flag & UN_LOW) != 0 ? - (n <= TBUF_LOW) : - (ch->ch_pun.un_flag & UN_TIME) != 0 ? - time_is_before_jiffies(ch->ch_waketime) : - (n == 0 && ch->ch_s_tpos == ch->ch_s_tin) && - ((ch->ch_pun.un_flag & UN_EMPTY) != 0 || - ((ch->ch_tun.un_open_count && - ch->ch_tun.un_tty->ops->chars_in_buffer) ? - (ch->ch_tun.un_tty->ops->chars_in_buffer)(ch->ch_tun.un_tty) == 0 - : 1 - ) - )) { - ch->ch_pun.un_flag &= ~(UN_EMPTY | UN_TIME | UN_LOW | UN_PWAIT); - - if (waitqueue_active(&((ch->ch_pun.un_tty)->write_wait))) - wake_up_interruptible(&((ch->ch_pun.un_tty)->write_wait)); - tty_wakeup(ch->ch_pun.un_tty); - n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK; - - } else if ((ch->ch_pun.un_flag & UN_TIME) != 0) { - work = 1; - } - } - - - /* - * Determine the max number of bytes - * this port can send, including - * packet header overhead. - */ - - t = ((ch->ch_s_tsize + ch->ch_s_tpos - ch->ch_s_tin) & 0xffff); - - if (n > t) - n = t; - - if (n != 0) { - n += (n <= 8 ? 1 : n <= 255 ? 2 : 3); - - tdata[tchan++] = n; - ttotal += n; - } - break; - - /* - * Close the port. - */ - -send_close: - case CS_SEND_CLOSE: - b = set_cmd_header(b, port, 10); - if (ch->ch_otype == OTYPE_IMMEDIATE) - *b++ = 3; - else - *b++ = 4; - - ch->ch_state = CS_WAIT_CLOSE; - break; - - /* - * Wait for a previous server request. - */ - - case CS_WAIT_OPEN: - case CS_WAIT_CANCEL: - case CS_WAIT_FAIL: - case CS_WAIT_QUERY: - case CS_WAIT_CLOSE: - break; - - default: - pr_info("%s - unexpected channel state (%i)\n", - __func__, ch->ch_state); - } - } - - /* - * If a module select code is needed, drop one in. If space - * was reserved for one, but none is needed, recover the space. - */ - - if (mod != nd->nd_tx_module) { - if (b != mbuf) { - mbuf[-1] = 0xf0 | mod; - nd->nd_tx_module = mod; - } else { - b--; - } - } - } - - /* - * Adjust "tmax" so that under worst case conditions we do - * not overflow either the daemon buffer or the internal - * buffer in the loop that follows. Leave a safe area - * of 64 bytes so we start getting asserts before we start - * losing data or clobbering memory. - */ - - n = UIO_MAX - UIO_BASE; - - if (tmax > n) - tmax = n; - - tmax -= 64; - - tsafe = tmax; - - /* - * Allocate space for 5 Module Selects, 1 Sequence Request, - * and 1 Set TREQ for each active channel. - */ - - tmax -= 5 + 3 + 4 * nd->nd_chan_count; - - /* - * Further reduce "tmax" to the available transmit credit. - * Note that this is a soft constraint; The transmit credit - * can go negative for a time and then recover. - */ - - n = nd->nd_tx_deposit - nd->nd_tx_charge - nd->nd_link.lk_header_size; - - if (tmax > n) - tmax = n; - - /* - * Finally reduce tmax by the number of bytes already in - * the buffer. - */ - - tmax -= b - buf; - - /* - * Suspend data transmit unless every ready channel can send - * at least 1 character. - */ - if (tmax < 2 * nd->nd_chan_count) { - tsend = 1; - - } else if (tchan > 1 && ttotal > tmax) { - - /* - * If transmit is limited by the credit budget, find the - * largest number of characters we can send without driving - * the credit negative. - */ - - long tm = tmax; - int tc = tchan; - int try; - - tsend = tm / tc; - - for (try = 0; try < 3; try++) { - int i; - int c = 0; - - for (i = 0; i < tc; i++) { - if (tsend < tdata[i]) - tdata[c++] = tdata[i]; - else - tm -= tdata[i]; - } - - if (c == tc) - break; - - tsend = tm / c; - - if (c == 1) - break; - - tc = c; - } - - tsend = tm / nd->nd_chan_count; - - if (tsend < 2) - tsend = 1; - - } else { - /* - * If no budgetary constraints, or only one channel ready - * to send, set the character limit to the remaining - * buffer size. - */ - - tsend = tmax; - } - - tsend -= (tsend <= 9) ? 1 : (tsend <= 257) ? 2 : 3; - - /* - * Loop over all channels, sending queued data. - */ - - port = 0; - ch = nd->nd_chan; - used_buffer = tmax; - - for (mod = 0; port < nd->nd_chan_count; mod++) { - /* - * If this is not the current module, enter a module select - * code in the buffer. - */ - - if (mod != nd->nd_tx_module) - mbuf = ++b; - - /* - * Loop to process one module. - */ - - maxport = port + 16; - - if (maxport > nd->nd_chan_count) - maxport = nd->nd_chan_count; - - for (; port < maxport; port++, ch++) { - if (ch->ch_state != CS_READY) - continue; - - lastport = port; - - n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK; - - /* - * If there is data that can be sent, send it. - */ - - if (n != 0 && used_buffer > 0) { - t = (ch->ch_s_tsize + ch->ch_s_tpos - ch->ch_s_tin) & 0xffff; - - if (n > t) - n = t; - - if (n > tsend) { - work = 1; - n = tsend; - } - - if (n > used_buffer) { - work = 1; - n = used_buffer; - } - - if (n <= 0) - continue; - - /* - * Create the correct size transmit header, - * depending on the amount of data to transmit. - */ - - if (n <= 8) { - - b[0] = ((n - 1) << 4) + (port & 0xf); - b += 1; - - } else if (n <= 255) { - - b[0] = 0x80 + (port & 0xf); - b[1] = n; - b += 2; - - } else { - - b[0] = 0x90 + (port & 0xf); - put_unaligned_be16(n, b + 1); - b += 3; - } - - ch->ch_s_tin = (ch->ch_s_tin + n) & 0xffff; - - /* - * Copy transmit data to the packet. - */ - - t = TBUF_MAX - ch->ch_tout; - - if (n >= t) { - memcpy(b, ch->ch_tbuf + ch->ch_tout, t); - b += t; - n -= t; - used_buffer -= t; - ch->ch_tout = 0; - } - - memcpy(b, ch->ch_tbuf + ch->ch_tout, n); - b += n; - used_buffer -= n; - ch->ch_tout += n; - n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK; - } - - /* - * Wake any terminal unit process waiting in the - * dgrp_write routine for low water. - */ - - if (n > TBUF_LOW) - continue; - - if ((ch->ch_flag & CH_LOW) != 0) { - ch->ch_flag &= ~CH_LOW; - wake_up_interruptible(&ch->ch_flag_wait); - } - - /* selwakeup tty_sel */ - if (ch->ch_tun.un_open_count) { - struct tty_struct *tty = (ch->ch_tun.un_tty); - - if (waitqueue_active(&tty->write_wait)) - wake_up_interruptible(&tty->write_wait); - - tty_wakeup(tty); - } - - if (ch->ch_pun.un_open_count) { - struct tty_struct *tty = (ch->ch_pun.un_tty); - - if (waitqueue_active(&tty->write_wait)) - wake_up_interruptible(&tty->write_wait); - - tty_wakeup(tty); - } - - /* - * Do EMPTY processing. - */ - - if (n != 0) - continue; - - if ((ch->ch_flag & (CH_EMPTY | CH_DRAIN)) != 0 || - (ch->ch_pun.un_flag & UN_EMPTY) != 0) { - /* - * If there is still data in the server, ask the server - * to notify us when its all gone. - */ - - if (ch->ch_s_treq != ch->ch_s_tin) { - b = set_cmd_header(b, port, 43); - - ch->ch_s_treq = ch->ch_s_tin; - put_unaligned_be16(ch->ch_s_treq, - b); - b += 2; - } - - /* - * If there is a thread waiting for buffer empty, - * and we are truly empty, wake the thread. - */ - - else if ((ch->ch_flag & CH_EMPTY) != 0 && - (ch->ch_send & RR_TX_BREAK) == 0) { - ch->ch_flag &= ~CH_EMPTY; - - wake_up_interruptible(&ch->ch_flag_wait); - } - } - } - - /* - * If a module select code is needed, drop one in. If space - * was reserved for one, but none is needed, recover the space. - */ - - if (mod != nd->nd_tx_module) { - if (b != mbuf) { - mbuf[-1] = 0xf0 | mod; - nd->nd_tx_module = mod; - } else { - b--; - } - } - } - - /* - * Send a synchronization sequence associated with the last open - * channel that sent data, and remember the time when the data was - * sent. - */ - - in = nd->nd_seq_in; - - if ((send_sync || nd->nd_seq_wait[in] != 0) && lastport >= 0) { - u8 *bb = b; - - /* - * Attempt the use the port that really wanted the sync. - * This gets around a race condition where the "lastport" is in - * the middle of the close() routine, and by the time we - * send this command, it will have already acked the close, and - * thus not send the sync response. - */ - if (wanted_sync_port >= 0) - lastport = wanted_sync_port; - /* - * Set a flag just in case the port is in the middle of a close, - * it will not be permitted to actually close until we get an - * sync response, and clear the flag there. - */ - ch = nd->nd_chan + lastport; - ch->ch_flag |= CH_WAITING_SYNC; - - mod = lastport >> 4; - - if (mod != nd->nd_tx_module) { - bb[0] = 0xf0 + mod; - bb += 1; - - nd->nd_tx_module = mod; - } - - bb = set_cmd_header(bb, lastport, 12); - *bb++ = in; - - nd->nd_seq_size[in] = bb - buf; - nd->nd_seq_time[in] = jiffies; - - if (++in >= SEQ_MAX) - in = 0; - - if (in != nd->nd_seq_out) { - b = bb; - nd->nd_seq_in = in; - nd->nd_unack += b - buf; - } - } - - /* - * If there are no open ports, a sync cannot be sent. - * There is nothing left to wait for anyway, so wake any - * thread waiting for an acknowledgement. - */ - - else if (nd->nd_seq_wait[in] != 0) { - nd->nd_seq_wait[in] = 0; - - wake_up_interruptible(&nd->nd_seq_wque[in]); - } - - /* - * If there is no traffic for an interval of IDLE_MAX, then - * send a single byte packet. - */ - - if (b != buf) { - nd->nd_tx_time = jiffies; - } else if ((ulong)(jiffies - nd->nd_tx_time) >= IDLE_MAX) { - *b++ = 0xf0 | nd->nd_tx_module; - nd->nd_tx_time = jiffies; - } - - n = b - buf; - - if (n >= tsafe) - pr_info("%s - n(%i) >= tsafe(%i)\n", - __func__, n, tsafe); - - if (tsend < 0) - dgrp_dump(buf, n); - - nd->nd_tx_work = work; - - return n; -} - -/* - * dgrp_net_read() - * Data to be sent TO the PortServer from the "async." half of the driver. - */ -static ssize_t dgrp_net_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - struct nd_struct *nd; - long n; - u8 *local_buf; - u8 *b; - ssize_t rtn; - - /* - * Get the node pointer, and quit if it doesn't exist. - */ - nd = (struct nd_struct *)(file->private_data); - if (!nd) - return -ENXIO; - - if (count < UIO_MIN) - return -EINVAL; - - /* - * Only one read/write operation may be in progress at - * any given time. - */ - - /* - * Grab the NET lock. - */ - down(&nd->nd_net_semaphore); - - nd->nd_read_count++; - - nd->nd_tx_ready = 0; - - /* - * Determine the effective size of the buffer. - */ - - if (nd->nd_remain > UIO_BASE) - pr_info_ratelimited("%s - nd_remain(%i) > UIO_BASE\n", - __func__, nd->nd_remain); - - b = local_buf = nd->nd_iobuf + UIO_BASE; - - /* - * Generate data according to the node state. - */ - - switch (nd->nd_state) { - /* - * Initialize the connection. - */ - - case NS_IDLE: - if (nd->nd_mon_buf) - dgrp_monitor_reset(nd); - - /* - * Request a Product ID Packet. - */ - - b[0] = 0xfb; - b[1] = 0x01; - b += 2; - - nd->nd_expect |= NR_IDENT; - - /* - * Request a Server Capability ID Response. - */ - - b[0] = 0xfb; - b[1] = 0x02; - b += 2; - - nd->nd_expect |= NR_CAPABILITY; - - /* - * Request a Server VPD Response. - */ - - b[0] = 0xfb; - b[1] = 0x18; - b += 2; - - nd->nd_expect |= NR_VPD; - - nd->nd_state = NS_WAIT_QUERY; - break; - - /* - * We do serious communication with the server only in - * the READY state. - */ - - case NS_READY: - b = dgrp_send(nd, count) + local_buf; - break; - - /* - * Send off an error after receiving a bogus message - * from the server. - */ - - case NS_SEND_ERROR: - n = strlen(nd->nd_error); - - b[0] = 0xff; - b[1] = n; - memcpy(b + 2, nd->nd_error, n); - b += 2 + n; - - dgrp_net_idle(nd); - /* - * Set the active port count to zero. - */ - dgrp_chan_count(nd, 0); - break; - - default: - break; - } - - n = b - local_buf; - - if (n != 0) { - nd->nd_send_count++; - - nd->nd_tx_byte += n + nd->nd_link.lk_header_size; - nd->nd_tx_charge += n + nd->nd_link.lk_header_size; - } - - rtn = copy_to_user((void __user *)buf, local_buf, n); - if (rtn) { - rtn = -EFAULT; - goto done; - } - - *ppos += n; - - rtn = n; - - if (nd->nd_mon_buf) - dgrp_monitor_data(nd, RPDUMP_CLIENT, local_buf, n); - - /* - * Release the NET lock. - */ -done: - up(&nd->nd_net_semaphore); - - return rtn; -} - -/** - * dgrp_receive() -- decode data packets received from the remote PortServer. - * @nd: pointer to a node structure - */ -static void dgrp_receive(struct nd_struct *nd) -{ - struct ch_struct *ch; - u8 *buf; - u8 *b; - u8 *dbuf; - char *error; - long port; - long dlen; - long plen; - long remain; - long n; - long mlast; - long elast; - long mstat; - long estat; - - char ID[3]; - - nd->nd_tx_time = jiffies; - - ID_TO_CHAR(nd->nd_ID, ID); - - b = buf = nd->nd_iobuf; - remain = nd->nd_remain; - - /* - * Loop to process Realport protocol packets. - */ - - while (remain > 0) { - int n0 = b[0] >> 4; - int n1 = b[0] & 0x0f; - - if (n0 <= 12) { - port = (nd->nd_rx_module << 4) + n1; - - if (port >= nd->nd_chan_count) { - error = "Improper Port Number"; - goto prot_error; - } - - ch = nd->nd_chan + port; - } else { - port = -1; - ch = NULL; - } - - /* - * Process by major packet type. - */ - - switch (n0) { - - /* - * Process 1-byte header data packet. - */ - - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - dlen = n0 + 1; - plen = dlen + 1; - - dbuf = b + 1; - goto data; - - /* - * Process 2-byte header data packet. - */ - - case 8: - if (remain < 3) - goto done; - - dlen = b[1]; - plen = dlen + 2; - - dbuf = b + 2; - goto data; - - /* - * Process 3-byte header data packet. - */ - - case 9: - if (remain < 4) - goto done; - - dlen = get_unaligned_be16(b + 1); - plen = dlen + 3; - - dbuf = b + 3; - - /* - * Common packet handling code. - */ - -data: - nd->nd_tx_work = 1; - - /* - * Otherwise data should appear only when we are - * in the CS_READY state. - */ - - if (ch->ch_state < CS_READY) { - error = "Data received before RWIN established"; - goto prot_error; - } - - /* - * Assure that the data received is within the - * allowable window. - */ - - n = (ch->ch_s_rwin - ch->ch_s_rin) & 0xffff; - - if (dlen > n) { - error = "Receive data overrun"; - goto prot_error; - } - - /* - * If we received 3 or less characters, - * assume it is a human typing, and set RTIME - * to 10 milliseconds. - * - * If we receive 10 or more characters, - * assume its not a human typing, and set RTIME - * to 100 milliseconds. - */ - - if (ch->ch_edelay != DGRP_RTIME) { - if (ch->ch_rtime != ch->ch_edelay) { - ch->ch_rtime = ch->ch_edelay; - ch->ch_flag |= CH_PARAM; - } - } else if (dlen <= 3) { - if (ch->ch_rtime != 10) { - ch->ch_rtime = 10; - ch->ch_flag |= CH_PARAM; - } - } else { - if (ch->ch_rtime != DGRP_RTIME) { - ch->ch_rtime = DGRP_RTIME; - ch->ch_flag |= CH_PARAM; - } - } - - /* - * If a portion of the packet is outside the - * buffer, shorten the effective length of the - * data packet to be the amount of data received. - */ - - if (remain < plen) - dlen -= plen - remain; - - /* - * Detect if receive flush is now complete. - */ - - if ((ch->ch_flag & CH_RX_FLUSH) != 0 && - ((ch->ch_flush_seq - nd->nd_seq_out) & SEQ_MASK) >= - ((nd->nd_seq_in - nd->nd_seq_out) & SEQ_MASK)) { - ch->ch_flag &= ~CH_RX_FLUSH; - } - - /* - * If we are ready to receive, move the data into - * the receive buffer. - */ - - ch->ch_s_rin = (ch->ch_s_rin + dlen) & 0xffff; - - if (ch->ch_state == CS_READY && - (ch->ch_tun.un_open_count != 0) && - (ch->ch_tun.un_flag & UN_CLOSING) == 0 && - (ch->ch_cflag & CF_CREAD) != 0 && - (ch->ch_flag & (CH_BAUD0 | CH_RX_FLUSH)) == 0 && - (ch->ch_send & RR_RX_FLUSH) == 0) { - - if (ch->ch_rin + dlen >= RBUF_MAX) { - n = RBUF_MAX - ch->ch_rin; - - memcpy(ch->ch_rbuf + ch->ch_rin, dbuf, n); - - ch->ch_rin = 0; - dbuf += n; - dlen -= n; - } - - memcpy(ch->ch_rbuf + ch->ch_rin, dbuf, dlen); - - ch->ch_rin += dlen; - - - /* - * If we are not in fastcook mode, or - * if there is a fastcook thread - * waiting for data, send the data to - * the line discipline. - */ - - if ((ch->ch_flag & CH_FAST_READ) == 0 || - ch->ch_inwait != 0) { - dgrp_input(ch); - } - - /* - * If there is a read thread waiting - * in select, and we are in fastcook - * mode, wake him up. - */ - - if (waitqueue_active(&ch->ch_tun.un_tty->read_wait) && - (ch->ch_flag & CH_FAST_READ) != 0) - wake_up_interruptible(&ch->ch_tun.un_tty->read_wait); - - /* - * Wake any thread waiting in the - * fastcook loop. - */ - - if ((ch->ch_flag & CH_INPUT) != 0) { - ch->ch_flag &= ~CH_INPUT; - - wake_up_interruptible(&ch->ch_flag_wait); - } - } - - /* - * Fabricate and insert a data packet header to - * preced the remaining data when it comes in. - */ - - if (remain < plen) { - dlen = plen - remain; - b = buf; - - b[0] = 0x90 + n1; - put_unaligned_be16(dlen, b + 1); - - remain = 3; - goto done; - } - break; - - /* - * Handle Window Sequence packets. - */ - - case 10: - plen = 3; - if (remain < plen) - goto done; - - nd->nd_tx_work = 1; - - { - ushort tpos = get_unaligned_be16(b + 1); - - ushort ack = (tpos - ch->ch_s_tpos) & 0xffff; - ushort unack = (ch->ch_s_tin - ch->ch_s_tpos) & 0xffff; - ushort notify = (ch->ch_s_treq - ch->ch_s_tpos) & 0xffff; - - if (ch->ch_state < CS_READY || ack > unack) { - error = "Improper Window Sequence"; - goto prot_error; - } - - ch->ch_s_tpos = tpos; - - if (notify <= ack) - ch->ch_s_treq = tpos; - } - break; - - /* - * Handle Command response packets. - */ - - case 11: - - /* - * RealPort engine fix - 03/11/2004 - * - * This check did not used to be here. - * - * We were using b[1] without verifying that the data - * is actually there and valid. On a split packet, it - * might not be yet. - * - * NOTE: I have never actually seen the failure happen - * under Linux, but since I have seen it occur - * under both Solaris and HP-UX, the assumption - * is that it *could* happen here as well... - */ - if (remain < 2) - goto done; - - - switch (b[1]) { - - /* - * Handle Open Response. - */ - - case 11: - plen = 6; - if (remain < plen) - goto done; - - nd->nd_tx_work = 1; - - { - int req = b[2]; - int resp = b[3]; - port = get_unaligned_be16(b + 4); - - if (port >= nd->nd_chan_count) { - error = "Open channel number out of range"; - goto prot_error; - } - - ch = nd->nd_chan + port; - - /* - * How we handle an open response depends primarily - * on our current channel state. - */ - - switch (ch->ch_state) { - case CS_IDLE: - - /* - * Handle a delayed open. - */ - - if (ch->ch_otype_waiting != 0 && - req == ch->ch_otype_waiting && - resp == 0) { - ch->ch_otype = req; - ch->ch_otype_waiting = 0; - ch->ch_state = CS_SEND_QUERY; - break; - } - goto open_error; - - case CS_WAIT_OPEN: - - /* - * Handle the open response. - */ - - if (req == ch->ch_otype) { - switch (resp) { - - /* - * On successful response, open the - * port and proceed normally. - */ - - case 0: - ch->ch_state = CS_SEND_QUERY; - break; - - /* - * On a busy response to a persistent open, - * remember that the open is pending. - */ - - case 1: - case 2: - if (req != OTYPE_IMMEDIATE) { - ch->ch_otype_waiting = req; - ch->ch_state = CS_IDLE; - break; - } - - /* - * Otherwise the server open failed. If - * the Unix port is open, hang it up. - */ - - default: - if (ch->ch_open_count != 0) { - ch->ch_flag |= CH_HANGUP; - dgrp_carrier(ch); - ch->ch_state = CS_IDLE; - break; - } - - ch->ch_open_error = resp; - ch->ch_state = CS_IDLE; - - wake_up_interruptible(&ch->ch_flag_wait); - } - break; - } - - /* - * Handle delayed response arrival preceding - * the open response we are waiting for. - */ - - if (ch->ch_otype_waiting != 0 && - req == ch->ch_otype_waiting && - resp == 0) { - ch->ch_otype = ch->ch_otype_waiting; - ch->ch_otype_waiting = 0; - ch->ch_state = CS_WAIT_FAIL; - break; - } - goto open_error; - - - case CS_WAIT_FAIL: - - /* - * Handle response to immediate open arriving - * after a delayed open success. - */ - - if (req == OTYPE_IMMEDIATE) { - ch->ch_state = CS_SEND_QUERY; - break; - } - goto open_error; - - - case CS_WAIT_CANCEL: - /* - * Handle delayed open response arriving before - * the cancel response. - */ - - if (req == ch->ch_otype_waiting && - resp == 0) { - ch->ch_otype_waiting = 0; - break; - } - - /* - * Handle cancel response. - */ - - if (req == 4 && resp == 0) { - ch->ch_otype_waiting = 0; - ch->ch_state = CS_IDLE; - break; - } - goto open_error; - - - case CS_WAIT_CLOSE: - /* - * Handle a successful response to a port - * close. - */ - - if (req >= 3) { - ch->ch_state = CS_IDLE; - break; - } - goto open_error; - -open_error: - default: - { - error = "Improper Open Response"; - goto prot_error; - } - } - } - break; - - /* - * Handle Synchronize Response. - */ - - case 13: - plen = 3; - if (remain < plen) - goto done; - { - int seq = b[2]; - int s; - - /* - * If channel was waiting for this sync response, - * unset the flag, and wake up anyone waiting - * on the event. - */ - if (ch->ch_flag & CH_WAITING_SYNC) { - ch->ch_flag &= ~(CH_WAITING_SYNC); - wake_up_interruptible(&ch->ch_flag_wait); - } - - if (((seq - nd->nd_seq_out) & SEQ_MASK) >= - ((nd->nd_seq_in - nd->nd_seq_out) & SEQ_MASK)) { - break; - } - - for (s = nd->nd_seq_out;; s = (s + 1) & SEQ_MASK) { - if (nd->nd_seq_wait[s] != 0) { - nd->nd_seq_wait[s] = 0; - - wake_up_interruptible(&nd->nd_seq_wque[s]); - } - - nd->nd_unack -= nd->nd_seq_size[s]; - - if (s == seq) - break; - } - - nd->nd_seq_out = (seq + 1) & SEQ_MASK; - } - break; - - /* - * Handle Sequence Response. - */ - - case 15: - plen = 6; - if (remain < plen) - goto done; - - { - /* Record that we have received the Sequence - * Response, but we aren't interested in the - * sequence numbers. We were using RIN like it - * was ROUT and that was causing problems, - * fixed 7-13-2001 David Fries. See comment in - * drp.h for ch_s_rin variable. - int rin = get_unaligned_be16(b + 2); - int tpos = get_unaligned_be16(b + 4); - */ - - ch->ch_send &= ~RR_SEQUENCE; - ch->ch_expect &= ~RR_SEQUENCE; - } - goto check_query; - - /* - * Handle Status Response. - */ - - case 17: - plen = 5; - if (remain < plen) - goto done; - - { - ch->ch_s_elast = get_unaligned_be16(b + 2); - ch->ch_s_mlast = b[4]; - - ch->ch_expect &= ~RR_STATUS; - ch->ch_send &= ~RR_STATUS; - - /* - * CH_PHYS_CD is cleared because something _could_ be - * waiting for the initial sense of carrier... and if - * carrier is high immediately, we want to be sure to - * wake them as soon as possible. - */ - ch->ch_flag &= ~CH_PHYS_CD; - - dgrp_carrier(ch); - } - goto check_query; - - /* - * Handle Line Error Response. - */ - - case 19: - plen = 14; - if (remain < plen) - goto done; - - break; - - /* - * Handle Buffer Response. - */ - - case 21: - plen = 6; - if (remain < plen) - goto done; - - { - ch->ch_s_rsize = get_unaligned_be16(b + 2); - ch->ch_s_tsize = get_unaligned_be16(b + 4); - - ch->ch_send &= ~RR_BUFFER; - ch->ch_expect &= ~RR_BUFFER; - } - goto check_query; - - /* - * Handle Port Capability Response. - */ - - case 23: - plen = 32; - if (remain < plen) - goto done; - - { - ch->ch_send &= ~RR_CAPABILITY; - ch->ch_expect &= ~RR_CAPABILITY; - } - - /* - * When all queries are complete, set those parameters - * derived from the query results, then transition - * to the READY state. - */ - -check_query: - if (ch->ch_state == CS_WAIT_QUERY && - (ch->ch_expect & (RR_SEQUENCE | - RR_STATUS | - RR_BUFFER | - RR_CAPABILITY)) == 0) { - ch->ch_tmax = ch->ch_s_tsize / 4; - - if (ch->ch_edelay == DGRP_TTIME) - ch->ch_ttime = DGRP_TTIME; - else - ch->ch_ttime = ch->ch_edelay; - - ch->ch_rmax = ch->ch_s_rsize / 4; - - if (ch->ch_edelay == DGRP_RTIME) - ch->ch_rtime = DGRP_RTIME; - else - ch->ch_rtime = ch->ch_edelay; - - ch->ch_rlow = 2 * ch->ch_s_rsize / 8; - ch->ch_rhigh = 6 * ch->ch_s_rsize / 8; - - ch->ch_state = CS_READY; - - nd->nd_tx_work = 1; - wake_up_interruptible(&ch->ch_flag_wait); - - } - break; - - default: - goto decode_error; - } - break; - - /* - * Handle Events. - */ - - case 12: - plen = 4; - if (remain < plen) - goto done; - - mlast = ch->ch_s_mlast; - elast = ch->ch_s_elast; - - mstat = ch->ch_s_mlast = b[1]; - estat = ch->ch_s_elast = get_unaligned_be16(b + 2); - - /* - * Handle modem changes. - */ - - if (((mstat ^ mlast) & DM_CD) != 0) - dgrp_carrier(ch); - - - /* - * Handle received break. - */ - - if ((estat & ~elast & EV_RXB) != 0 && - (ch->ch_tun.un_open_count != 0) && - I_BRKINT(ch->ch_tun.un_tty) && - !(I_IGNBRK(ch->ch_tun.un_tty))) { - - tty_buffer_request_room(&ch->port, 1); - tty_insert_flip_char(&ch->port, 0, TTY_BREAK); - tty_flip_buffer_push(&ch->port); - - } - - /* - * On transmit break complete, if more break traffic - * is waiting then send it. Otherwise wake any threads - * waiting for transmitter empty. - */ - - if ((~estat & elast & EV_TXB) != 0 && - (ch->ch_expect & RR_TX_BREAK) != 0) { - - nd->nd_tx_work = 1; - - ch->ch_expect &= ~RR_TX_BREAK; - - if (ch->ch_break_time != 0) { - ch->ch_send |= RR_TX_BREAK; - } else { - ch->ch_send &= ~RR_TX_BREAK; - ch->ch_flag &= ~CH_TX_BREAK; - wake_up_interruptible(&ch->ch_flag_wait); - } - } - break; - - case 13: - case 14: - error = "Unrecognized command"; - goto prot_error; - - /* - * Decode Special Codes. - */ - - case 15: - switch (n1) { - /* - * One byte module select. - */ - - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - plen = 1; - nd->nd_rx_module = n1; - break; - - /* - * Two byte module select. - */ - - case 8: - plen = 2; - if (remain < plen) - goto done; - - nd->nd_rx_module = b[1]; - break; - - /* - * ID Request packet. - */ - - case 11: - if (remain < 4) - goto done; - - plen = get_unaligned_be16(b + 2); - - if (plen < 12 || plen > 1000) { - error = "Response Packet length error"; - goto prot_error; - } - - nd->nd_tx_work = 1; - - switch (b[1]) { - /* - * Echo packet. - */ - - case 0: - nd->nd_send |= NR_ECHO; - break; - - /* - * ID Response packet. - */ - - case 1: - nd->nd_send |= NR_IDENT; - break; - - /* - * ID Response packet. - */ - - case 32: - nd->nd_send |= NR_PASSWORD; - break; - - } - break; - - /* - * Various node-level response packets. - */ - - case 12: - if (remain < 4) - goto done; - - plen = get_unaligned_be16(b + 2); - - if (plen < 4 || plen > 1000) { - error = "Response Packet length error"; - goto prot_error; - } - - nd->nd_tx_work = 1; - - switch (b[1]) { - /* - * Echo packet. - */ - - case 0: - nd->nd_expect &= ~NR_ECHO; - break; - - /* - * Product Response Packet. - */ - - case 1: - { - int desclen; - - nd->nd_hw_ver = (b[8] << 8) | b[9]; - nd->nd_sw_ver = (b[10] << 8) | b[11]; - nd->nd_hw_id = b[6]; - desclen = (plen - 12 > MAX_DESC_LEN - 1) ? MAX_DESC_LEN - 1 : - plen - 12; - - if (desclen <= 0) { - error = "Response Packet desclen error"; - goto prot_error; - } - - strncpy(nd->nd_ps_desc, b + 12, desclen); - nd->nd_ps_desc[desclen] = 0; - } - - nd->nd_expect &= ~NR_IDENT; - break; - - /* - * Capability Response Packet. - */ - - case 2: - { - int nn = get_unaligned_be16(b + 4); - - if (nn > CHAN_MAX) - nn = CHAN_MAX; - - dgrp_chan_count(nd, nn); - } - - nd->nd_expect &= ~NR_CAPABILITY; - break; - - /* - * VPD Response Packet. - */ - - case 15: - /* - * NOTE: case 15 is here ONLY because the EtherLite - * is broken, and sends a response to 24 back as 15. - * To resolve this, the EtherLite firmware is now - * fixed to send back 24 correctly, but, for backwards - * compatibility, we now have reserved 15 for the - * bad EtherLite response to 24 as well. - */ - - /* Fallthru! */ - - case 24: - - /* - * If the product doesn't support VPD, - * it will send back a null IDRESP, - * which is a length of 4 bytes. - */ - if (plen > 4) { - memcpy(nd->nd_vpd, b + 4, min(plen - 4, (long) VPDSIZE)); - nd->nd_vpd_len = min(plen - 4, (long) VPDSIZE); - } - - nd->nd_expect &= ~NR_VPD; - break; - - default: - goto decode_error; - } - - if (nd->nd_expect == 0 && - nd->nd_state == NS_WAIT_QUERY) { - nd->nd_state = NS_READY; - } - break; - - /* - * Debug packet. - */ - - case 14: - if (remain < 4) - goto done; - - plen = get_unaligned_be16(b + 2) + 4; - - if (plen > 1000) { - error = "Debug Packet too large"; - goto prot_error; - } - - if (remain < plen) - goto done; - break; - - /* - * Handle reset packet. - */ - - case 15: - if (remain < 2) - goto done; - - plen = 2 + b[1]; - - if (remain < plen) - goto done; - - nd->nd_tx_work = 1; - - n = b[plen]; - b[plen] = 0; - - b[plen] = n; - - error = "Client Reset Acknowledge"; - goto prot_error; - - default: - goto decode_error; - } - break; - - default: - goto decode_error; - } - - b += plen; - remain -= plen; - } - - /* - * When the buffer is exhausted, copy any data left at the - * top of the buffer back down to the bottom for the next - * read request. - */ - -done: - if (remain > 0 && b != buf) - memcpy(buf, b, remain); - - nd->nd_remain = remain; - return; - -/* - * Handle a decode error. - */ - -decode_error: - error = "Protocol decode error"; - -/* - * Handle a general protocol error. - */ - -prot_error: - nd->nd_remain = 0; - nd->nd_state = NS_SEND_ERROR; - nd->nd_error = error; -} - -/* - * dgrp_net_write() -- write data to the network device. - * - * A zero byte write indicates that the connection to the RealPort - * device has been broken. - * - * A non-zero write indicates data from the RealPort device. - */ -static ssize_t dgrp_net_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct nd_struct *nd; - ssize_t rtn = 0; - long n; - long total = 0; - - /* - * Get the node pointer, and quit if it doesn't exist. - */ - nd = (struct nd_struct *)(file->private_data); - if (!nd) - return -ENXIO; - - /* - * Grab the NET lock. - */ - down(&nd->nd_net_semaphore); - - nd->nd_write_count++; - - /* - * Handle disconnect. - */ - - if (count == 0) { - dgrp_net_idle(nd); - /* - * Set the active port count to zero. - */ - dgrp_chan_count(nd, 0); - goto unlock; - } - - /* - * Loop to process entire receive packet. - */ - - while (count > 0) { - n = UIO_MAX - nd->nd_remain; - - if (n > count) - n = count; - - nd->nd_rx_byte += n + nd->nd_link.lk_header_size; - - rtn = copy_from_user(nd->nd_iobuf + nd->nd_remain, - (void __user *) buf + total, n); - if (rtn) { - rtn = -EFAULT; - goto unlock; - } - - *ppos += n; - - total += n; - - count -= n; - - if (nd->nd_mon_buf) - dgrp_monitor_data(nd, RPDUMP_SERVER, - nd->nd_iobuf + nd->nd_remain, n); - - nd->nd_remain += n; - - dgrp_receive(nd); - } - - rtn = total; - -unlock: - /* - * Release the NET lock. - */ - up(&nd->nd_net_semaphore); - - return rtn; -} - - -/* - * dgrp_net_select() - * Determine whether a device is ready to be read or written to, and - * sleep if not. - */ -static unsigned int dgrp_net_select(struct file *file, - struct poll_table_struct *table) -{ - unsigned int retval = 0; - struct nd_struct *nd = file->private_data; - - poll_wait(file, &nd->nd_tx_waitq, table); - - if (nd->nd_tx_ready) - retval |= POLLIN | POLLRDNORM; /* Conditionally readable */ - - retval |= POLLOUT | POLLWRNORM; /* Always writeable */ - - return retval; -} - -/* - * dgrp_net_ioctl - * - * Implement those functions which allow the network daemon to control - * the network parameters in the driver. The ioctls include ones to - * get and set the link speed parameters for the PortServer. - */ -static long dgrp_net_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct nd_struct *nd; - int rtn = 0; - long size = _IOC_SIZE(cmd); - struct link_struct link; - - nd = file->private_data; - - if (_IOC_DIR(cmd) & _IOC_READ) - rtn = access_ok(VERIFY_WRITE, (void __user *) arg, size); - else if (_IOC_DIR(cmd) & _IOC_WRITE) - rtn = access_ok(VERIFY_READ, (void __user *) arg, size); - - if (!rtn) - return rtn; - - switch (cmd) { - case DIGI_SETLINK: - if (size != sizeof(struct link_struct)) - return -EINVAL; - - if (copy_from_user(&link, (void __user *)arg, size)) - return -EFAULT; - - if (link.lk_fast_rate < 9600) - link.lk_fast_rate = 9600; - - if (link.lk_slow_rate < 2400) - link.lk_slow_rate = 2400; - - if (link.lk_fast_rate > 10000000) - link.lk_fast_rate = 10000000; - - if (link.lk_slow_rate > link.lk_fast_rate) - link.lk_slow_rate = link.lk_fast_rate; - - if (link.lk_fast_delay > 2000) - link.lk_fast_delay = 2000; - - if (link.lk_slow_delay > 10000) - link.lk_slow_delay = 10000; - - if (link.lk_fast_delay < 60) - link.lk_fast_delay = 60; - - if (link.lk_slow_delay < link.lk_fast_delay) - link.lk_slow_delay = link.lk_fast_delay; - - if (link.lk_header_size < 2) - link.lk_header_size = 2; - - if (link.lk_header_size > 128) - link.lk_header_size = 128; - - link.lk_fast_rate /= 8 * 1000 / dgrp_poll_tick; - link.lk_slow_rate /= 8 * 1000 / dgrp_poll_tick; - - link.lk_fast_delay /= dgrp_poll_tick; - link.lk_slow_delay /= dgrp_poll_tick; - - nd->nd_link = link; - - break; - - case DIGI_GETLINK: - if (size != sizeof(struct link_struct)) - return -EINVAL; - - if (copy_to_user((void __user *)arg, (void *)(&nd->nd_link), - size)) - return -EFAULT; - - break; - - default: - return -EINVAL; - - } - - return 0; -} - -/** - * dgrp_poll_handler() -- handler for poll timer - * - * As each timer expires, it determines (a) whether the "transmit" - * waiter needs to be woken up, and (b) whether the poller needs to - * be rescheduled. - */ -void dgrp_poll_handler(unsigned long arg) -{ - struct dgrp_poll_data *poll_data; - struct nd_struct *nd; - struct link_struct *lk; - ulong time; - ulong poll_time; - ulong freq; - ulong lock_flags; - - poll_data = (struct dgrp_poll_data *) arg; - freq = 1000 / poll_data->poll_tick; - poll_data->poll_round += 17; - - if (poll_data->poll_round >= freq) - poll_data->poll_round -= freq; - - /* - * Loop to process all open nodes. - * - * For each node, determine the rate at which it should - * be transmitting data. Then if the node should wake up - * and transmit data now, enable the net receive select - * to get the transmit going. - */ - - list_for_each_entry(nd, &nd_struct_list, list) { - - lk = &nd->nd_link; - - /* - * Decrement statistics. These are only for use with - * KME, so don't worry that the operations are done - * unlocked, and so the results are occasionally wrong. - */ - - nd->nd_read_count -= (nd->nd_read_count + - poll_data->poll_round) / freq; - nd->nd_write_count -= (nd->nd_write_count + - poll_data->poll_round) / freq; - nd->nd_send_count -= (nd->nd_send_count + - poll_data->poll_round) / freq; - nd->nd_tx_byte -= (nd->nd_tx_byte + - poll_data->poll_round) / freq; - nd->nd_rx_byte -= (nd->nd_rx_byte + - poll_data->poll_round) / freq; - - /* - * Wake the daemon to transmit data only when there is - * enough byte credit to send data. - * - * The results are approximate because the operations - * are performed unlocked, and we are inspecting - * data asynchronously updated elsewhere. The whole - * thing is just approximation anyway, so that should - * be okay. - */ - - if (lk->lk_slow_rate >= UIO_MAX) { - - nd->nd_delay = 0; - nd->nd_rate = UIO_MAX; - - nd->nd_tx_deposit = nd->nd_tx_charge + 3 * UIO_MAX; - nd->nd_tx_credit = 3 * UIO_MAX; - - } else { - - long rate; - long delay; - long deposit; - long charge; - long size; - long excess; - - long seq_in = nd->nd_seq_in; - long seq_out = nd->nd_seq_out; - - /* - * If there are no outstanding packets, run at the - * fastest rate. - */ - - if (seq_in == seq_out) { - delay = 0; - rate = lk->lk_fast_rate; - } - - /* - * Otherwise compute the transmit rate based on the - * delay since the oldest packet. - */ - - else { - /* - * The actual delay is computed as the - * time since the oldest unacknowledged - * packet was sent, minus the time it - * took to send that packet to the server. - */ - - delay = ((jiffies - nd->nd_seq_time[seq_out]) - - (nd->nd_seq_size[seq_out] / - lk->lk_fast_rate)); - - /* - * If the delay is less than the "fast" - * delay, transmit full speed. If greater - * than the "slow" delay, transmit at the - * "slow" speed. In between, interpolate - * between the fast and slow speeds. - */ - - rate = - (delay <= lk->lk_fast_delay ? - lk->lk_fast_rate : - delay >= lk->lk_slow_delay ? - lk->lk_slow_rate : - (lk->lk_slow_rate + - (lk->lk_slow_delay - delay) * - (lk->lk_fast_rate - lk->lk_slow_rate) / - (lk->lk_slow_delay - lk->lk_fast_delay) - ) - ); - } - - nd->nd_delay = delay; - nd->nd_rate = rate; - - /* - * Increase the transmit credit by depositing the - * current transmit rate. - */ - - deposit = nd->nd_tx_deposit; - charge = nd->nd_tx_charge; - - deposit += rate; - - /* - * If the available transmit credit becomes too large, - * reduce the deposit to correct the value. - * - * Too large is the max of: - * 6 times the header size - * 3 times the current transmit rate. - */ - - size = 2 * nd->nd_link.lk_header_size; - - if (size < rate) - size = rate; - - size *= 3; - - excess = deposit - charge - size; - - if (excess > 0) - deposit -= excess; - - nd->nd_tx_deposit = deposit; - nd->nd_tx_credit = deposit - charge; - - /* - * Wake the transmit task only if the transmit credit - * is at least 3 times the transmit header size. - */ - - size = 3 * lk->lk_header_size; - - if (nd->nd_tx_credit < size) - continue; - } - - - /* - * Enable the READ select to wake the daemon if there - * is useful work for the drp_read routine to perform. - */ - - if (waitqueue_active(&nd->nd_tx_waitq) && - (nd->nd_tx_work != 0 || - (ulong)(jiffies - nd->nd_tx_time) >= IDLE_MAX)) { - nd->nd_tx_ready = 1; - - wake_up_interruptible(&nd->nd_tx_waitq); - - /* not needed */ - /* nd->nd_flag &= ~ND_SELECT; */ - } - } - - - /* - * Schedule ourself back at the nominal wakeup interval. - */ - spin_lock_irqsave(&poll_data->poll_lock, lock_flags); - - poll_data->node_active_count--; - if (poll_data->node_active_count > 0) { - poll_data->node_active_count++; - poll_time = poll_data->timer.expires + - poll_data->poll_tick * HZ / 1000; - - time = poll_time - jiffies; - - if (time >= 2 * poll_data->poll_tick) - poll_time = jiffies + dgrp_poll_tick * HZ / 1000; - - poll_data->timer.expires = poll_time; - add_timer(&poll_data->timer); - } - - spin_unlock_irqrestore(&poll_data->poll_lock, lock_flags); -} |