/* * * Copyright 1999 Digi International (www.digi.com) * James Puzzo * * 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 #include #include #include #include #include #include #include #include #include #include #include #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 ? ((jiffies - ch->ch_waketime) >= 0) : (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) ? MAX_DESC_LEN : 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); }