3667 lines
73 KiB
C
3667 lines
73 KiB
C
/*
|
|
*
|
|
* 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);
|
|
}
|