staging: Add dgrp driver for Digi Realport devices
This is based on dgrp-1.9 available from ftp://ftp1.digi.com/support/beta/linux/dgrp/dgrp-1.9.tgz Signed-off-by: Bill Pemberton <wfp5p@virginia.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
5de69349ed
commit
0b52b74972
|
@ -0,0 +1,9 @@
|
|||
config DGRP
|
||||
tristate "Digi Realport driver"
|
||||
default n
|
||||
depends on SYSFS
|
||||
---help---
|
||||
Support for Digi Realport devices. These devices allow you to
|
||||
access remote serial ports as if they are local tty devices. This
|
||||
will build the kernel driver, you will still need the userspace
|
||||
component to make your Realport device work.
|
|
@ -0,0 +1,12 @@
|
|||
obj-$(CONFIG_DGRP) += dgrp.o
|
||||
|
||||
dgrp-y := \
|
||||
dgrp_common.o \
|
||||
dgrp_dpa_ops.o \
|
||||
dgrp_driver.o \
|
||||
dgrp_mon_ops.o \
|
||||
dgrp_net_ops.o \
|
||||
dgrp_ports_ops.o \
|
||||
dgrp_specproc.o \
|
||||
dgrp_tty.o \
|
||||
dgrp_sysfs.o
|
|
@ -0,0 +1,2 @@
|
|||
The user space code to work with this driver is located at
|
||||
https://github.com/wfp5p/dgrp-utils
|
|
@ -0,0 +1,7 @@
|
|||
- Use configfs for config stuff. This will require changes to the
|
||||
user space code.
|
||||
|
||||
- Check the calls to tty_register_device. In particular, check to see
|
||||
if there should be some handling for IS_ERR(classp).
|
||||
|
||||
- dgrp_send() and dgrp_receive() could use some refactoring
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
*
|
||||
* 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_common.c
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Definitions of global variables and functions which are either
|
||||
* shared by the tty, mon, and net drivers; or which cross them
|
||||
* functionally (like the poller).
|
||||
*
|
||||
* Author:
|
||||
*
|
||||
* James A. Puzzo
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/cred.h>
|
||||
|
||||
#include "dgrp_common.h"
|
||||
|
||||
/**
|
||||
* dgrp_carrier -- check for carrier change state and act
|
||||
* @ch: struct ch_struct *
|
||||
*/
|
||||
void dgrp_carrier(struct ch_struct *ch)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
|
||||
int virt_carrier = 0;
|
||||
int phys_carrier = 0;
|
||||
|
||||
/* fix case when the tty has already closed. */
|
||||
|
||||
if (!ch)
|
||||
return;
|
||||
nd = ch->ch_nd;
|
||||
if (!nd)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If we are currently waiting to determine the status of the port,
|
||||
* we don't yet know the state of the modem lines. As a result,
|
||||
* we ignore state changes when we are waiting for the modem lines
|
||||
* to be established. We know, as a result of code in dgrp_net_ops,
|
||||
* that we will be called again immediately following the reception
|
||||
* of the status message with the true modem status flags in it.
|
||||
*/
|
||||
if (ch->ch_expect & RR_STATUS)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If CH_HANGUP is set, we gotta keep trying to get all the processes
|
||||
* that have the port open to close the port.
|
||||
* So lets just keep sending a hangup every time we get here.
|
||||
*/
|
||||
if ((ch->ch_flag & CH_HANGUP) &&
|
||||
(ch->ch_tun.un_open_count > 0))
|
||||
tty_hangup(ch->ch_tun.un_tty);
|
||||
|
||||
/*
|
||||
* Compute the effective state of both the physical and virtual
|
||||
* senses of carrier.
|
||||
*/
|
||||
|
||||
if (ch->ch_s_mlast & DM_CD)
|
||||
phys_carrier = 1;
|
||||
|
||||
if ((ch->ch_s_mlast & DM_CD) ||
|
||||
(ch->ch_digi.digi_flags & DIGI_FORCEDCD) ||
|
||||
(ch->ch_flag & CH_CLOCAL))
|
||||
virt_carrier = 1;
|
||||
|
||||
/*
|
||||
* Test for a VIRTUAL carrier transition to HIGH.
|
||||
*
|
||||
* The CH_HANGUP condition is intended to prevent any action
|
||||
* except for close. As a result, we ignore positive carrier
|
||||
* transitions during CH_HANGUP.
|
||||
*/
|
||||
if (((ch->ch_flag & CH_HANGUP) == 0) &&
|
||||
((ch->ch_flag & CH_VIRT_CD) == 0) &&
|
||||
(virt_carrier == 1)) {
|
||||
/*
|
||||
* When carrier rises, wake any threads waiting
|
||||
* for carrier in the open routine.
|
||||
*/
|
||||
nd->nd_tx_work = 1;
|
||||
|
||||
if (waitqueue_active(&ch->ch_flag_wait))
|
||||
wake_up_interruptible(&ch->ch_flag_wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test for a PHYSICAL transition to low, so long as we aren't
|
||||
* currently ignoring physical transitions (which is what "virtual
|
||||
* carrier" indicates).
|
||||
*
|
||||
* The transition of the virtual carrier to low really doesn't
|
||||
* matter... it really only means "ignore carrier state", not
|
||||
* "make pretend that carrier is there".
|
||||
*/
|
||||
if ((virt_carrier == 0) &&
|
||||
((ch->ch_flag & CH_PHYS_CD) != 0) &&
|
||||
(phys_carrier == 0)) {
|
||||
/*
|
||||
* When carrier drops:
|
||||
*
|
||||
* Do a Hard Hangup if that is called for.
|
||||
*
|
||||
* Drop carrier on all open units.
|
||||
*
|
||||
* Flush queues, waking up any task waiting in the
|
||||
* line discipline.
|
||||
*
|
||||
* Send a hangup to the control terminal.
|
||||
*
|
||||
* Enable all select calls.
|
||||
*/
|
||||
|
||||
nd->nd_tx_work = 1;
|
||||
|
||||
ch->ch_flag &= ~(CH_LOW | CH_EMPTY | CH_DRAIN | CH_INPUT);
|
||||
|
||||
if (waitqueue_active(&ch->ch_flag_wait))
|
||||
wake_up_interruptible(&ch->ch_flag_wait);
|
||||
|
||||
if (ch->ch_tun.un_open_count > 0)
|
||||
tty_hangup(ch->ch_tun.un_tty);
|
||||
|
||||
if (ch->ch_pun.un_open_count > 0)
|
||||
tty_hangup(ch->ch_pun.un_tty);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure that our cached values reflect the current reality.
|
||||
*/
|
||||
if (virt_carrier == 1)
|
||||
ch->ch_flag |= CH_VIRT_CD;
|
||||
else
|
||||
ch->ch_flag &= ~CH_VIRT_CD;
|
||||
|
||||
if (phys_carrier == 1)
|
||||
ch->ch_flag |= CH_PHYS_CD;
|
||||
else
|
||||
ch->ch_flag &= ~CH_PHYS_CD;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* dgrp_chk_perm() -- check permissions for net device
|
||||
* @inode: pointer to inode structure for the net communication device
|
||||
* @op: operation to be tested
|
||||
*
|
||||
* The file permissions and ownerships are tested to determine whether
|
||||
* the operation "op" is permitted on the file pointed to by the inode.
|
||||
* Returns 0 if the operation is permitted, -EACCESS otherwise
|
||||
*/
|
||||
int dgrp_chk_perm(int mode, int op)
|
||||
{
|
||||
if (!current_euid())
|
||||
mode >>= 6;
|
||||
else if (in_egroup_p(0))
|
||||
mode >>= 3;
|
||||
|
||||
if ((mode & op & 0007) == op)
|
||||
return 0;
|
||||
|
||||
if (capable(CAP_SYS_ADMIN))
|
||||
return 0;
|
||||
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/* dgrp_chk_perm wrapper for permission call in struct inode_operations */
|
||||
int dgrp_inode_permission(struct inode *inode, int op)
|
||||
{
|
||||
return dgrp_chk_perm(inode->i_mode, op);
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __DGRP_COMMON_H
|
||||
#define __DGRP_COMMON_H
|
||||
|
||||
#define DIGI_VERSION "1.9-29"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/timer.h>
|
||||
#include "drp.h"
|
||||
|
||||
#define DGRP_TTIME 100
|
||||
#define DGRP_RTIME 100
|
||||
|
||||
/************************************************************************
|
||||
* All global storage allocation.
|
||||
************************************************************************/
|
||||
|
||||
extern int dgrp_rawreadok; /* Allow raw writing of input */
|
||||
extern int dgrp_register_cudevices; /* enable legacy cu devices */
|
||||
extern int dgrp_register_prdevices; /* enable transparent print devices */
|
||||
extern int dgrp_poll_tick; /* Poll interval - in ms */
|
||||
|
||||
extern struct list_head nd_struct_list;
|
||||
|
||||
struct dgrp_poll_data {
|
||||
spinlock_t poll_lock;
|
||||
struct timer_list timer;
|
||||
int poll_tick;
|
||||
ulong poll_round; /* Timer rouding factor */
|
||||
long node_active_count;
|
||||
};
|
||||
|
||||
extern struct dgrp_poll_data dgrp_poll_data;
|
||||
extern void dgrp_poll_handler(unsigned long arg);
|
||||
|
||||
/* from dgrp_mon_ops.c */
|
||||
extern void dgrp_register_mon_hook(struct proc_dir_entry *de);
|
||||
|
||||
/* from dgrp_tty.c */
|
||||
extern int dgrp_tty_init(struct nd_struct *nd);
|
||||
extern void dgrp_tty_uninit(struct nd_struct *nd);
|
||||
|
||||
/* from dgrp_ports_ops.c */
|
||||
extern void dgrp_register_ports_hook(struct proc_dir_entry *de);
|
||||
|
||||
/* from dgrp_net_ops.c */
|
||||
extern void dgrp_register_net_hook(struct proc_dir_entry *de);
|
||||
|
||||
/* from dgrp_dpa_ops.c */
|
||||
extern void dgrp_register_dpa_hook(struct proc_dir_entry *de);
|
||||
extern void dgrp_dpa_data(struct nd_struct *, int, u8 *, int);
|
||||
|
||||
/* from dgrp_sysfs.c */
|
||||
extern void dgrp_create_class_sysfs_files(void);
|
||||
extern void dgrp_remove_class_sysfs_files(void);
|
||||
|
||||
extern void dgrp_create_node_class_sysfs_files(struct nd_struct *nd);
|
||||
extern void dgrp_remove_node_class_sysfs_files(struct nd_struct *nd);
|
||||
|
||||
extern void dgrp_create_tty_sysfs(struct un_struct *un, struct device *c);
|
||||
extern void dgrp_remove_tty_sysfs(struct device *c);
|
||||
|
||||
/* from dgrp_specproc.c */
|
||||
/*
|
||||
* The list of DGRP entries with r/w capabilities. These
|
||||
* magic numbers are used for identification purposes.
|
||||
*/
|
||||
enum {
|
||||
DGRP_CONFIG = 1, /* Configure portservers */
|
||||
DGRP_NETDIR = 2, /* Directory for "net" devices */
|
||||
DGRP_MONDIR = 3, /* Directory for "mon" devices */
|
||||
DGRP_PORTSDIR = 4, /* Directory for "ports" devices */
|
||||
DGRP_INFO = 5, /* Get info. about the running module */
|
||||
DGRP_NODEINFO = 6, /* Get info. about the configured nodes */
|
||||
DGRP_DPADIR = 7, /* Directory for the "dpa" devices */
|
||||
};
|
||||
|
||||
/*
|
||||
* Directions for proc handlers
|
||||
*/
|
||||
enum {
|
||||
INBOUND = 1, /* Data being written to kernel */
|
||||
OUTBOUND = 2, /* Data being read from the kernel */
|
||||
};
|
||||
|
||||
/**
|
||||
* dgrp_proc_entry: structure for dgrp proc dirs
|
||||
* @id: ID number associated with this particular entry. Should be
|
||||
* unique across all of DGRP.
|
||||
* @name: text name associated with the /proc entry
|
||||
* @mode: file access permisssions for the /proc entry
|
||||
* @child: pointer to table describing a subdirectory for this entry
|
||||
* @de: pointer to directory entry for this object once registered. Used
|
||||
* to grab the handle of the object for unregistration
|
||||
* @excl_sem: semaphore to provide exclusive to struct
|
||||
* @excl_cnt: counter of current accesses
|
||||
*
|
||||
* Each entry in a DGRP proc directory is described with a
|
||||
* dgrp_proc_entry structure. A collection of these
|
||||
* entries (in an array) represents the members associated
|
||||
* with a particular /proc directory, and is referred to
|
||||
* as a table. All tables are terminated by an entry with
|
||||
* zeros for every member.
|
||||
*/
|
||||
struct dgrp_proc_entry {
|
||||
int id; /* Integer identifier */
|
||||
const char *name; /* ASCII identifier */
|
||||
mode_t mode; /* File access permissions */
|
||||
struct dgrp_proc_entry *child; /* Child pointer */
|
||||
|
||||
/* file ops to use, pass NULL to use default */
|
||||
struct file_operations *proc_file_ops;
|
||||
|
||||
struct proc_dir_entry *de; /* proc entry pointer */
|
||||
struct semaphore excl_sem; /* Protects exclusive access var */
|
||||
int excl_cnt; /* Counts number of curr accesses */
|
||||
};
|
||||
|
||||
extern void dgrp_unregister_proc(void);
|
||||
extern void dgrp_register_proc(void);
|
||||
|
||||
/*-----------------------------------------------------------------------*
|
||||
*
|
||||
* Declarations for common operations:
|
||||
*
|
||||
* (either used by more than one of net, mon, or tty,
|
||||
* or in interrupt context (i.e. the poller))
|
||||
*
|
||||
*-----------------------------------------------------------------------*/
|
||||
|
||||
void dgrp_carrier(struct ch_struct *ch);
|
||||
extern int dgrp_inode_permission(struct inode *inode, int op);
|
||||
extern int dgrp_chk_perm(int mode, int op);
|
||||
|
||||
|
||||
/*
|
||||
* ID manipulation macros (where c1 & c2 are characters, i is
|
||||
* a long integer, and s is a character array of at least three members
|
||||
*/
|
||||
|
||||
static inline void ID_TO_CHAR(long i, char *s)
|
||||
{
|
||||
s[0] = ((i & 0xff00)>>8);
|
||||
s[1] = (i & 0xff);
|
||||
s[2] = 0;
|
||||
}
|
||||
|
||||
static inline long CHAR_TO_ID(char *s)
|
||||
{
|
||||
return ((s[0] & 0xff) << 8) | (s[1] & 0xff);
|
||||
}
|
||||
|
||||
static inline struct nd_struct *nd_struct_get(long major)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
|
||||
list_for_each_entry(nd, &nd_struct_list, list) {
|
||||
if (major == nd->nd_major)
|
||||
return nd;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int nd_struct_add(struct nd_struct *entry)
|
||||
{
|
||||
struct nd_struct *ptr;
|
||||
|
||||
ptr = nd_struct_get(entry->nd_major);
|
||||
|
||||
if (ptr)
|
||||
return -EBUSY;
|
||||
|
||||
list_add_tail(&entry->list, &nd_struct_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int nd_struct_del(struct nd_struct *entry)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
|
||||
nd = nd_struct_get(entry->nd_major);
|
||||
|
||||
if (!nd)
|
||||
return -ENODEV;
|
||||
|
||||
list_del(&nd->list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* __DGRP_COMMON_H */
|
|
@ -0,0 +1,556 @@
|
|||
/*
|
||||
*
|
||||
* 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_dpa_ops.c
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Handle the file operations required for the "dpa" devices.
|
||||
* Includes those functions required to register the "dpa" devices
|
||||
* in "/proc".
|
||||
*
|
||||
* Author:
|
||||
*
|
||||
* James A. Puzzo
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "dgrp_common.h"
|
||||
|
||||
/* File operation declarations */
|
||||
static int dgrp_dpa_open(struct inode *, struct file *);
|
||||
static int dgrp_dpa_release(struct inode *, struct file *);
|
||||
static ssize_t dgrp_dpa_read(struct file *, char __user *, size_t, loff_t *);
|
||||
static long dgrp_dpa_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
static unsigned int dgrp_dpa_select(struct file *, struct poll_table_struct *);
|
||||
|
||||
static const struct file_operations dpa_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = dgrp_dpa_read,
|
||||
.poll = dgrp_dpa_select,
|
||||
.unlocked_ioctl = dgrp_dpa_ioctl,
|
||||
.open = dgrp_dpa_open,
|
||||
.release = dgrp_dpa_release,
|
||||
};
|
||||
|
||||
static struct inode_operations dpa_inode_ops = {
|
||||
.permission = dgrp_inode_permission
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct digi_node {
|
||||
uint nd_state; /* Node state: 1 = up, 0 = down. */
|
||||
uint nd_chan_count; /* Number of channels found */
|
||||
uint nd_tx_byte; /* Tx data count */
|
||||
uint nd_rx_byte; /* RX data count */
|
||||
u8 nd_ps_desc[MAX_DESC_LEN]; /* Description from PS */
|
||||
};
|
||||
|
||||
#define DIGI_GETNODE (('d'<<8) | 249) /* get board info */
|
||||
|
||||
|
||||
struct digi_chan {
|
||||
uint ch_port; /* Port number to get info on */
|
||||
uint ch_open; /* 1 if open, 0 if not */
|
||||
uint ch_txcount; /* TX data count */
|
||||
uint ch_rxcount; /* RX data count */
|
||||
uint ch_s_brate; /* Realport BRATE */
|
||||
uint ch_s_estat; /* Realport ELAST */
|
||||
uint ch_s_cflag; /* Realport CFLAG */
|
||||
uint ch_s_iflag; /* Realport IFLAG */
|
||||
uint ch_s_oflag; /* Realport OFLAG */
|
||||
uint ch_s_xflag; /* Realport XFLAG */
|
||||
uint ch_s_mstat; /* Realport MLAST */
|
||||
};
|
||||
|
||||
#define DIGI_GETCHAN (('d'<<8) | 248) /* get channel info */
|
||||
|
||||
|
||||
struct digi_vpd {
|
||||
int vpd_len;
|
||||
char vpd_data[VPDSIZE];
|
||||
};
|
||||
|
||||
#define DIGI_GETVPD (('d'<<8) | 246) /* get VPD info */
|
||||
|
||||
|
||||
struct digi_debug {
|
||||
int onoff;
|
||||
int port;
|
||||
};
|
||||
|
||||
#define DIGI_SETDEBUG (('d'<<8) | 247) /* set debug info */
|
||||
|
||||
|
||||
void dgrp_register_dpa_hook(struct proc_dir_entry *de)
|
||||
{
|
||||
struct nd_struct *node = de->data;
|
||||
|
||||
de->proc_iops = &dpa_inode_ops;
|
||||
de->proc_fops = &dpa_ops;
|
||||
|
||||
node->nd_dpa_de = de;
|
||||
spin_lock_init(&node->nd_dpa_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* dgrp_dpa_open -- open the DPA device for a particular PortServer
|
||||
*/
|
||||
static int dgrp_dpa_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
int rtn = 0;
|
||||
|
||||
struct proc_dir_entry *de;
|
||||
|
||||
rtn = try_module_get(THIS_MODULE);
|
||||
if (!rtn)
|
||||
return -ENXIO;
|
||||
|
||||
rtn = 0;
|
||||
|
||||
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.
|
||||
*/
|
||||
de = PDE(inode);
|
||||
if (!de) {
|
||||
rtn = -ENXIO;
|
||||
goto done;
|
||||
}
|
||||
nd = (struct nd_struct *)de->data;
|
||||
if (!nd) {
|
||||
rtn = -ENXIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
file->private_data = (void *) nd;
|
||||
|
||||
/*
|
||||
* Allocate the DPA buffer.
|
||||
*/
|
||||
|
||||
if (nd->nd_dpa_buf) {
|
||||
rtn = -EBUSY;
|
||||
} else {
|
||||
nd->nd_dpa_buf = kmalloc(DPA_MAX, GFP_KERNEL);
|
||||
|
||||
if (!nd->nd_dpa_buf) {
|
||||
rtn = -ENOMEM;
|
||||
} else {
|
||||
nd->nd_dpa_out = 0;
|
||||
nd->nd_dpa_in = 0;
|
||||
nd->nd_dpa_lbolt = jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
if (rtn)
|
||||
module_put(THIS_MODULE);
|
||||
return rtn;
|
||||
}
|
||||
|
||||
/*
|
||||
* dgrp_dpa_release -- close the DPA device for a particular PortServer
|
||||
*/
|
||||
static int dgrp_dpa_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
u8 *buf;
|
||||
unsigned long lock_flags;
|
||||
|
||||
/*
|
||||
* Get the node pointer, and quit if it doesn't exist.
|
||||
*/
|
||||
nd = (struct nd_struct *)(file->private_data);
|
||||
if (!nd)
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* Free the dpa buffer.
|
||||
*/
|
||||
|
||||
spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
|
||||
|
||||
buf = nd->nd_dpa_buf;
|
||||
|
||||
nd->nd_dpa_buf = NULL;
|
||||
nd->nd_dpa_out = nd->nd_dpa_in;
|
||||
|
||||
/*
|
||||
* Wakeup any thread waiting for buffer space.
|
||||
*/
|
||||
|
||||
if (nd->nd_dpa_flag & DPA_WAIT_SPACE) {
|
||||
nd->nd_dpa_flag &= ~DPA_WAIT_SPACE;
|
||||
wake_up_interruptible(&nd->nd_dpa_wqueue);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
|
||||
|
||||
kfree(buf);
|
||||
|
||||
done:
|
||||
module_put(THIS_MODULE);
|
||||
file->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* dgrp_dpa_read
|
||||
*
|
||||
* Copy data from the monitoring buffer to the user, freeing space
|
||||
* in the monitoring buffer for more messages
|
||||
*/
|
||||
static ssize_t dgrp_dpa_read(struct file *file, char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
int n;
|
||||
int r;
|
||||
int offset = 0;
|
||||
int res = 0;
|
||||
ssize_t rtn;
|
||||
unsigned long lock_flags;
|
||||
|
||||
/*
|
||||
* Get the node pointer, and quit if it doesn't exist.
|
||||
*/
|
||||
nd = (struct nd_struct *)(file->private_data);
|
||||
if (!nd)
|
||||
return -ENXIO;
|
||||
|
||||
/*
|
||||
* Wait for some data to appear in the buffer.
|
||||
*/
|
||||
|
||||
spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
|
||||
|
||||
for (;;) {
|
||||
n = (nd->nd_dpa_in - nd->nd_dpa_out) & DPA_MASK;
|
||||
|
||||
if (n != 0)
|
||||
break;
|
||||
|
||||
nd->nd_dpa_flag |= DPA_WAIT_DATA;
|
||||
|
||||
spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
|
||||
|
||||
/*
|
||||
* Go to sleep waiting until the condition becomes true.
|
||||
*/
|
||||
rtn = wait_event_interruptible(nd->nd_dpa_wqueue,
|
||||
((nd->nd_dpa_flag & DPA_WAIT_DATA) == 0));
|
||||
|
||||
if (rtn)
|
||||
return rtn;
|
||||
|
||||
spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read whatever is there.
|
||||
*/
|
||||
|
||||
if (n > count)
|
||||
n = count;
|
||||
|
||||
res = n;
|
||||
|
||||
r = DPA_MAX - nd->nd_dpa_out;
|
||||
|
||||
if (r <= n) {
|
||||
|
||||
spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
|
||||
rtn = copy_to_user((void __user *)buf,
|
||||
nd->nd_dpa_buf + nd->nd_dpa_out, r);
|
||||
spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
|
||||
|
||||
if (rtn) {
|
||||
rtn = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
nd->nd_dpa_out = 0;
|
||||
n -= r;
|
||||
offset = r;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
|
||||
rtn = copy_to_user((void __user *)buf + offset,
|
||||
nd->nd_dpa_buf + nd->nd_dpa_out, n);
|
||||
spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
|
||||
|
||||
if (rtn) {
|
||||
rtn = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
nd->nd_dpa_out += n;
|
||||
|
||||
*ppos += res;
|
||||
|
||||
rtn = res;
|
||||
|
||||
/*
|
||||
* Wakeup any thread waiting for buffer space.
|
||||
*/
|
||||
|
||||
n = (nd->nd_dpa_in - nd->nd_dpa_out) & DPA_MASK;
|
||||
|
||||
if (nd->nd_dpa_flag & DPA_WAIT_SPACE &&
|
||||
(DPA_MAX - n) > DPA_HIGH_WATER) {
|
||||
nd->nd_dpa_flag &= ~DPA_WAIT_SPACE;
|
||||
wake_up_interruptible(&nd->nd_dpa_wqueue);
|
||||
}
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
|
||||
return rtn;
|
||||
}
|
||||
|
||||
static unsigned int dgrp_dpa_select(struct file *file,
|
||||
struct poll_table_struct *table)
|
||||
{
|
||||
unsigned int retval = 0;
|
||||
struct nd_struct *nd = file->private_data;
|
||||
|
||||
if (nd->nd_dpa_out != nd->nd_dpa_in)
|
||||
retval |= POLLIN | POLLRDNORM; /* Conditionally readable */
|
||||
|
||||
retval |= POLLOUT | POLLWRNORM; /* Always writeable */
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static long dgrp_dpa_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
|
||||
struct nd_struct *nd;
|
||||
struct digi_chan getchan;
|
||||
struct digi_node getnode;
|
||||
struct ch_struct *ch;
|
||||
struct digi_debug setdebug;
|
||||
struct digi_vpd vpd;
|
||||
unsigned int port;
|
||||
void __user *uarg = (void __user *) arg;
|
||||
|
||||
nd = file->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case DIGI_GETCHAN:
|
||||
if (copy_from_user(&getchan, uarg, sizeof(struct digi_chan)))
|
||||
return -EFAULT;
|
||||
|
||||
port = getchan.ch_port;
|
||||
|
||||
if (port < 0 || port > nd->nd_chan_count)
|
||||
return -EINVAL;
|
||||
|
||||
ch = nd->nd_chan + port;
|
||||
|
||||
getchan.ch_open = (ch->ch_open_count > 0) ? 1 : 0;
|
||||
getchan.ch_txcount = ch->ch_txcount;
|
||||
getchan.ch_rxcount = ch->ch_rxcount;
|
||||
getchan.ch_s_brate = ch->ch_s_brate;
|
||||
getchan.ch_s_estat = ch->ch_s_elast;
|
||||
getchan.ch_s_cflag = ch->ch_s_cflag;
|
||||
getchan.ch_s_iflag = ch->ch_s_iflag;
|
||||
getchan.ch_s_oflag = ch->ch_s_oflag;
|
||||
getchan.ch_s_xflag = ch->ch_s_xflag;
|
||||
getchan.ch_s_mstat = ch->ch_s_mlast;
|
||||
|
||||
if (copy_to_user(uarg, &getchan, sizeof(struct digi_chan)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
|
||||
|
||||
case DIGI_GETNODE:
|
||||
getnode.nd_state = (nd->nd_state & NS_READY) ? 1 : 0;
|
||||
getnode.nd_chan_count = nd->nd_chan_count;
|
||||
getnode.nd_tx_byte = nd->nd_tx_byte;
|
||||
getnode.nd_rx_byte = nd->nd_rx_byte;
|
||||
|
||||
memset(&getnode.nd_ps_desc, 0, MAX_DESC_LEN);
|
||||
strncpy(getnode.nd_ps_desc, nd->nd_ps_desc, MAX_DESC_LEN);
|
||||
|
||||
if (copy_to_user(uarg, &getnode, sizeof(struct digi_node)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
|
||||
|
||||
case DIGI_SETDEBUG:
|
||||
if (copy_from_user(&setdebug, uarg, sizeof(struct digi_debug)))
|
||||
return -EFAULT;
|
||||
|
||||
nd->nd_dpa_debug = setdebug.onoff;
|
||||
nd->nd_dpa_port = setdebug.port;
|
||||
break;
|
||||
|
||||
|
||||
case DIGI_GETVPD:
|
||||
if (nd->nd_vpd_len > 0) {
|
||||
vpd.vpd_len = nd->nd_vpd_len;
|
||||
memcpy(&vpd.vpd_data, &nd->nd_vpd, nd->nd_vpd_len);
|
||||
} else {
|
||||
vpd.vpd_len = 0;
|
||||
}
|
||||
|
||||
if (copy_to_user(uarg, &vpd, sizeof(struct digi_vpd)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dgrp_dpa() -- send data to the device monitor queue
|
||||
* @nd: pointer to a node structure
|
||||
* @buf: buffer of 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_dpa(struct nd_struct *nd, u8 *buf, int nbuf)
|
||||
{
|
||||
int n;
|
||||
int r;
|
||||
unsigned long lock_flags;
|
||||
|
||||
/*
|
||||
* Grab DPA lock.
|
||||
*/
|
||||
spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
|
||||
|
||||
/*
|
||||
* Loop while data remains.
|
||||
*/
|
||||
while (nbuf > 0 && nd->nd_dpa_buf != NULL) {
|
||||
|
||||
n = (nd->nd_dpa_out - nd->nd_dpa_in - 1) & DPA_MASK;
|
||||
|
||||
/*
|
||||
* Enforce flow control on the DPA device.
|
||||
*/
|
||||
if (n < (DPA_MAX - DPA_HIGH_WATER))
|
||||
nd->nd_dpa_flag |= DPA_WAIT_SPACE;
|
||||
|
||||
/*
|
||||
* This should never happen, as the flow control above
|
||||
* should have stopped things before they got to this point.
|
||||
*/
|
||||
if (n == 0) {
|
||||
spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy as much data as will fit.
|
||||
*/
|
||||
|
||||
if (n > nbuf)
|
||||
n = nbuf;
|
||||
|
||||
r = DPA_MAX - nd->nd_dpa_in;
|
||||
|
||||
if (r <= n) {
|
||||
memcpy(nd->nd_dpa_buf + nd->nd_dpa_in, buf, r);
|
||||
|
||||
n -= r;
|
||||
|
||||
nd->nd_dpa_in = 0;
|
||||
|
||||
buf += r;
|
||||
nbuf -= r;
|
||||
}
|
||||
|
||||
memcpy(nd->nd_dpa_buf + nd->nd_dpa_in, buf, n);
|
||||
|
||||
nd->nd_dpa_in += n;
|
||||
|
||||
buf += n;
|
||||
nbuf -= n;
|
||||
|
||||
if (nd->nd_dpa_in >= DPA_MAX)
|
||||
pr_info_ratelimited("%s - nd->nd_dpa_in (%i) >= DPA_MAX\n",
|
||||
__func__, nd->nd_dpa_in);
|
||||
|
||||
/*
|
||||
* Wakeup any thread waiting for data
|
||||
*/
|
||||
if (nd->nd_dpa_flag & DPA_WAIT_DATA) {
|
||||
nd->nd_dpa_flag &= ~DPA_WAIT_DATA;
|
||||
wake_up_interruptible(&nd->nd_dpa_wqueue);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the DPA lock.
|
||||
*/
|
||||
spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* dgrp_monitor_data() -- builds a DPA data packet
|
||||
* @nd: pointer to a node structure
|
||||
* @type: type of message to be logged in the DPA buffer
|
||||
* @buf: buffer of data to be logged in the DPA buffer
|
||||
* @size -- number of bytes in the "buf" buffer
|
||||
*/
|
||||
void dgrp_dpa_data(struct nd_struct *nd, int type, u8 *buf, int size)
|
||||
{
|
||||
u8 header[5];
|
||||
|
||||
header[0] = type;
|
||||
|
||||
put_unaligned_be32(size, header + 1);
|
||||
|
||||
dgrp_dpa(nd, header, sizeof(header));
|
||||
dgrp_dpa(nd, buf, size);
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 1999-2003 Digi International (www.digi.com)
|
||||
* Jeff Randall
|
||||
* James Puzzo <jamesp at digi dot com>
|
||||
* Scott Kilau <Scott_Kilau 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Driver specific includes
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
/*
|
||||
* PortServer includes
|
||||
*/
|
||||
#include "dgrp_common.h"
|
||||
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Digi International, http://www.digi.com");
|
||||
MODULE_DESCRIPTION("RealPort driver for Digi's ethernet-based serial connectivity product line");
|
||||
MODULE_VERSION(DIGI_VERSION);
|
||||
|
||||
struct list_head nd_struct_list;
|
||||
struct dgrp_poll_data dgrp_poll_data;
|
||||
|
||||
int dgrp_rawreadok = 1; /* Bypass flipbuf on input */
|
||||
int dgrp_register_cudevices = 1;/* Turn on/off registering legacy cu devices */
|
||||
int dgrp_register_prdevices = 1;/* Turn on/off registering transparent print */
|
||||
int dgrp_poll_tick = 20; /* Poll interval - in ms */
|
||||
|
||||
module_param_named(rawreadok, dgrp_rawreadok, int, 0644);
|
||||
MODULE_PARM_DESC(rawreadok, "Bypass flip buffers on input");
|
||||
|
||||
module_param_named(register_cudevices, dgrp_register_cudevices, int, 0644);
|
||||
MODULE_PARM_DESC(register_cudevices, "Turn on/off registering legacy cu devices");
|
||||
|
||||
module_param_named(register_prdevices, dgrp_register_prdevices, int, 0644);
|
||||
MODULE_PARM_DESC(register_prdevices, "Turn on/off registering transparent print devices");
|
||||
|
||||
module_param_named(pollrate, dgrp_poll_tick, int, 0644);
|
||||
MODULE_PARM_DESC(pollrate, "Poll interval in ms");
|
||||
|
||||
/* Driver load/unload functions */
|
||||
static int dgrp_init_module(void);
|
||||
static void dgrp_cleanup_module(void);
|
||||
|
||||
module_init(dgrp_init_module);
|
||||
module_exit(dgrp_cleanup_module);
|
||||
|
||||
/*
|
||||
* init_module()
|
||||
*
|
||||
* Module load. This is where it all starts.
|
||||
*/
|
||||
static int dgrp_init_module(void)
|
||||
{
|
||||
INIT_LIST_HEAD(&nd_struct_list);
|
||||
|
||||
spin_lock_init(&dgrp_poll_data.poll_lock);
|
||||
init_timer(&dgrp_poll_data.timer);
|
||||
dgrp_poll_data.poll_tick = dgrp_poll_tick;
|
||||
dgrp_poll_data.timer.function = dgrp_poll_handler;
|
||||
dgrp_poll_data.timer.data = (unsigned long) &dgrp_poll_data;
|
||||
|
||||
dgrp_create_class_sysfs_files();
|
||||
|
||||
dgrp_register_proc();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Module unload. This is where it all ends.
|
||||
*/
|
||||
static void dgrp_cleanup_module(void)
|
||||
{
|
||||
struct nd_struct *nd, *next;
|
||||
|
||||
/*
|
||||
* Attempting to free resources in backwards
|
||||
* order of allocation, in case that helps
|
||||
* memory pool fragmentation.
|
||||
*/
|
||||
dgrp_unregister_proc();
|
||||
|
||||
dgrp_remove_class_sysfs_files();
|
||||
|
||||
|
||||
list_for_each_entry_safe(nd, next, &nd_struct_list, list) {
|
||||
dgrp_tty_uninit(nd);
|
||||
kfree(nd);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,346 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* 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_mon_ops.c
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Handle the file operations required for the "monitor" devices.
|
||||
* Includes those functions required to register the "mon" devices
|
||||
* in "/proc".
|
||||
*
|
||||
* Author:
|
||||
*
|
||||
* James A. Puzzo
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
#include "dgrp_common.h"
|
||||
|
||||
/* File operation declarations */
|
||||
static int dgrp_mon_open(struct inode *, struct file *);
|
||||
static int dgrp_mon_release(struct inode *, struct file *);
|
||||
static ssize_t dgrp_mon_read(struct file *, char __user *, size_t, loff_t *);
|
||||
static long dgrp_mon_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
|
||||
static const struct file_operations mon_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = dgrp_mon_read,
|
||||
.unlocked_ioctl = dgrp_mon_ioctl,
|
||||
.open = dgrp_mon_open,
|
||||
.release = dgrp_mon_release,
|
||||
};
|
||||
|
||||
static struct inode_operations mon_inode_ops = {
|
||||
.permission = dgrp_inode_permission
|
||||
};
|
||||
|
||||
void dgrp_register_mon_hook(struct proc_dir_entry *de)
|
||||
{
|
||||
struct nd_struct *node = de->data;
|
||||
|
||||
de->proc_iops = &mon_inode_ops;
|
||||
de->proc_fops = &mon_ops;
|
||||
node->nd_mon_de = de;
|
||||
sema_init(&node->nd_mon_semaphore, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* dgrp_mon_open() -- open /proc/dgrp/ports device for a PortServer
|
||||
* @inode: struct inode *
|
||||
* @file: struct file *
|
||||
*
|
||||
* Open function to open the /proc/dgrp/ports device for a PortServer.
|
||||
*/
|
||||
static int dgrp_mon_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
struct proc_dir_entry *de;
|
||||
struct timeval tv;
|
||||
uint32_t time;
|
||||
u8 *buf;
|
||||
int rtn;
|
||||
|
||||
rtn = try_module_get(THIS_MODULE);
|
||||
if (!rtn)
|
||||
return -ENXIO;
|
||||
|
||||
rtn = 0;
|
||||
|
||||
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.
|
||||
*/
|
||||
de = PDE(inode);
|
||||
if (!de) {
|
||||
rtn = -ENXIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
nd = (struct nd_struct *)de->data;
|
||||
if (!nd) {
|
||||
rtn = -ENXIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
file->private_data = (void *) nd;
|
||||
|
||||
/*
|
||||
* Allocate the monitor buffer.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Grab the MON lock.
|
||||
*/
|
||||
down(&nd->nd_mon_semaphore);
|
||||
|
||||
if (nd->nd_mon_buf) {
|
||||
rtn = -EBUSY;
|
||||
goto done_up;
|
||||
}
|
||||
|
||||
nd->nd_mon_buf = kmalloc(MON_MAX, GFP_KERNEL);
|
||||
|
||||
if (!nd->nd_mon_buf) {
|
||||
rtn = -ENOMEM;
|
||||
goto done_up;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enter an RPDUMP file header into the buffer.
|
||||
*/
|
||||
|
||||
buf = nd->nd_mon_buf;
|
||||
|
||||
strcpy(buf, RPDUMP_MAGIC);
|
||||
buf += strlen(buf) + 1;
|
||||
|
||||
do_gettimeofday(&tv);
|
||||
|
||||
/*
|
||||
* tv.tv_sec might be a 64 bit quantity. Pare
|
||||
* it down to 32 bits before attempting to encode
|
||||
* it.
|
||||
*/
|
||||
time = (uint32_t) (tv.tv_sec & 0xffffffff);
|
||||
|
||||
put_unaligned_be32(time, buf);
|
||||
put_unaligned_be16(0, buf + 4);
|
||||
buf += 6;
|
||||
|
||||
if (nd->nd_tx_module) {
|
||||
buf[0] = RPDUMP_CLIENT;
|
||||
put_unaligned_be32(0, buf + 1);
|
||||
put_unaligned_be16(1, buf + 5);
|
||||
buf[7] = 0xf0 + nd->nd_tx_module;
|
||||
buf += 8;
|
||||
}
|
||||
|
||||
if (nd->nd_rx_module) {
|
||||
buf[0] = RPDUMP_SERVER;
|
||||
put_unaligned_be32(0, buf + 1);
|
||||
put_unaligned_be16(1, buf + 5);
|
||||
buf[7] = 0xf0 + nd->nd_rx_module;
|
||||
buf += 8;
|
||||
}
|
||||
|
||||
nd->nd_mon_out = 0;
|
||||
nd->nd_mon_in = buf - nd->nd_mon_buf;
|
||||
nd->nd_mon_lbolt = jiffies;
|
||||
|
||||
done_up:
|
||||
up(&nd->nd_mon_semaphore);
|
||||
|
||||
done:
|
||||
if (rtn)
|
||||
module_put(THIS_MODULE);
|
||||
return rtn;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* dgrp_mon_release() - Close the MON device for a particular PortServer
|
||||
* @inode: struct inode *
|
||||
* @file: struct file *
|
||||
*/
|
||||
static int dgrp_mon_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
|
||||
/*
|
||||
* Get the node pointer, and quit if it doesn't exist.
|
||||
*/
|
||||
nd = (struct nd_struct *)(file->private_data);
|
||||
if (!nd)
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* Free the monitor buffer.
|
||||
*/
|
||||
|
||||
down(&nd->nd_mon_semaphore);
|
||||
|
||||
kfree(nd->nd_mon_buf);
|
||||
nd->nd_mon_buf = NULL;
|
||||
nd->nd_mon_out = nd->nd_mon_in;
|
||||
|
||||
/*
|
||||
* Wakeup any thread waiting for buffer space.
|
||||
*/
|
||||
|
||||
if (nd->nd_mon_flag & MON_WAIT_SPACE) {
|
||||
nd->nd_mon_flag &= ~MON_WAIT_SPACE;
|
||||
wake_up_interruptible(&nd->nd_mon_wqueue);
|
||||
}
|
||||
|
||||
up(&nd->nd_mon_semaphore);
|
||||
|
||||
/*
|
||||
* Make sure there is no thread in the middle of writing a packet.
|
||||
*/
|
||||
down(&nd->nd_net_semaphore);
|
||||
up(&nd->nd_net_semaphore);
|
||||
|
||||
done:
|
||||
module_put(THIS_MODULE);
|
||||
file->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dgrp_mon_read() -- Copy data from the monitoring buffer to the user
|
||||
*/
|
||||
static ssize_t dgrp_mon_read(struct file *file, char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
int r;
|
||||
int offset = 0;
|
||||
int res = 0;
|
||||
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;
|
||||
|
||||
/*
|
||||
* Wait for some data to appear in the buffer.
|
||||
*/
|
||||
|
||||
down(&nd->nd_mon_semaphore);
|
||||
|
||||
for (;;) {
|
||||
res = (nd->nd_mon_in - nd->nd_mon_out) & MON_MASK;
|
||||
|
||||
if (res)
|
||||
break;
|
||||
|
||||
nd->nd_mon_flag |= MON_WAIT_DATA;
|
||||
|
||||
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_DATA) == 0));
|
||||
|
||||
if (rtn)
|
||||
return rtn;
|
||||
|
||||
down(&nd->nd_mon_semaphore);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read whatever is there.
|
||||
*/
|
||||
|
||||
if (res > count)
|
||||
res = count;
|
||||
|
||||
r = MON_MAX - nd->nd_mon_out;
|
||||
|
||||
if (r <= res) {
|
||||
rtn = copy_to_user((void __user *)buf,
|
||||
nd->nd_mon_buf + nd->nd_mon_out, r);
|
||||
if (rtn) {
|
||||
up(&nd->nd_mon_semaphore);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
nd->nd_mon_out = 0;
|
||||
res -= r;
|
||||
offset = r;
|
||||
}
|
||||
|
||||
rtn = copy_to_user((void __user *) buf + offset,
|
||||
nd->nd_mon_buf + nd->nd_mon_out, res);
|
||||
if (rtn) {
|
||||
up(&nd->nd_mon_semaphore);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
nd->nd_mon_out += res;
|
||||
|
||||
*ppos += res;
|
||||
|
||||
up(&nd->nd_mon_semaphore);
|
||||
|
||||
/*
|
||||
* Wakeup any thread waiting for buffer space.
|
||||
*/
|
||||
|
||||
if (nd->nd_mon_flag & MON_WAIT_SPACE) {
|
||||
nd->nd_mon_flag &= ~MON_WAIT_SPACE;
|
||||
wake_up_interruptible(&nd->nd_mon_wqueue);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ioctl is not valid on monitor device */
|
||||
static long dgrp_mon_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 1999-2000 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* Filename:
|
||||
*
|
||||
* dgrp_ports_ops.c
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Handle the file operations required for the /proc/dgrp/ports/...
|
||||
* devices. Basically gathers tty status for the node and returns it.
|
||||
*
|
||||
* Author:
|
||||
*
|
||||
* James A. Puzzo
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include "dgrp_common.h"
|
||||
|
||||
/* File operation declarations */
|
||||
static int dgrp_ports_open(struct inode *, struct file *);
|
||||
|
||||
static const struct file_operations ports_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = dgrp_ports_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release
|
||||
};
|
||||
|
||||
static struct inode_operations ports_inode_ops = {
|
||||
.permission = dgrp_inode_permission
|
||||
};
|
||||
|
||||
|
||||
void dgrp_register_ports_hook(struct proc_dir_entry *de)
|
||||
{
|
||||
struct nd_struct *node = de->data;
|
||||
|
||||
de->proc_iops = &ports_inode_ops;
|
||||
de->proc_fops = &ports_ops;
|
||||
node->nd_ports_de = de;
|
||||
}
|
||||
|
||||
static void *dgrp_ports_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
if (*pos == 0)
|
||||
seq_puts(seq, "#num tty_open pr_open tot_wait MSTAT IFLAG OFLAG CFLAG BPS DIGIFLAGS\n");
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void *dgrp_ports_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
struct nd_struct *nd = seq->private;
|
||||
|
||||
if (*pos >= nd->nd_chan_count)
|
||||
return NULL;
|
||||
|
||||
*pos += 1;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void dgrp_ports_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
}
|
||||
|
||||
static int dgrp_ports_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
loff_t *pos = v;
|
||||
struct nd_struct *nd;
|
||||
struct ch_struct *ch;
|
||||
struct un_struct *tun, *pun;
|
||||
unsigned int totcnt;
|
||||
|
||||
nd = seq->private;
|
||||
if (!nd)
|
||||
return 0;
|
||||
|
||||
if (*pos >= nd->nd_chan_count)
|
||||
return 0;
|
||||
|
||||
ch = &nd->nd_chan[*pos];
|
||||
tun = &ch->ch_tun;
|
||||
pun = &ch->ch_pun;
|
||||
|
||||
/*
|
||||
* If port is not open and no one is waiting to
|
||||
* open it, the modem signal values can't be
|
||||
* trusted, and will be zeroed.
|
||||
*/
|
||||
totcnt = tun->un_open_count +
|
||||
pun->un_open_count +
|
||||
ch->ch_wait_count[0] +
|
||||
ch->ch_wait_count[1] +
|
||||
ch->ch_wait_count[2];
|
||||
|
||||
seq_printf(seq, "%02d %02d %02d %02d 0x%04X 0x%04X 0x%04X 0x%04X %-6d 0x%04X\n",
|
||||
(int) *pos,
|
||||
tun->un_open_count,
|
||||
pun->un_open_count,
|
||||
ch->ch_wait_count[0] +
|
||||
ch->ch_wait_count[1] +
|
||||
ch->ch_wait_count[2],
|
||||
(totcnt ? ch->ch_s_mlast : 0),
|
||||
ch->ch_s_iflag,
|
||||
ch->ch_s_oflag,
|
||||
ch->ch_s_cflag,
|
||||
(ch->ch_s_brate ? (1843200 / ch->ch_s_brate) : 0),
|
||||
ch->ch_digi.digi_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations ports_seq_ops = {
|
||||
.start = dgrp_ports_seq_start,
|
||||
.next = dgrp_ports_seq_next,
|
||||
.stop = dgrp_ports_seq_stop,
|
||||
.show = dgrp_ports_seq_show,
|
||||
};
|
||||
|
||||
/**
|
||||
* dgrp_ports_open -- open the /proc/dgrp/ports/... device
|
||||
* @inode: struct inode *
|
||||
* @file: struct file *
|
||||
*
|
||||
* Open function to open the /proc/dgrp/ports device for a PortServer.
|
||||
* This is the open function for struct file_operations
|
||||
*/
|
||||
static int dgrp_ports_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq;
|
||||
int rtn;
|
||||
|
||||
rtn = seq_open(file, &ports_seq_ops);
|
||||
if (!rtn) {
|
||||
seq = file->private_data;
|
||||
seq->private = PDE(inode)->data;
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
|
@ -0,0 +1,821 @@
|
|||
/*
|
||||
*
|
||||
* 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_specproc.c
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Handle the "config" proc entry for the linux realport device driver
|
||||
* and provide slots for the "net" and "mon" devices
|
||||
*
|
||||
* Author:
|
||||
*
|
||||
* James A. Puzzo
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include "dgrp_common.h"
|
||||
|
||||
static struct dgrp_proc_entry dgrp_table[];
|
||||
static struct proc_dir_entry *dgrp_proc_dir_entry;
|
||||
|
||||
static int dgrp_add_id(long id);
|
||||
static int dgrp_remove_nd(struct nd_struct *nd);
|
||||
static void unregister_dgrp_device(struct proc_dir_entry *de);
|
||||
static void register_dgrp_device(struct nd_struct *node,
|
||||
struct proc_dir_entry *root,
|
||||
void (*register_hook)(struct proc_dir_entry *de));
|
||||
|
||||
/* File operation declarations */
|
||||
static int dgrp_gen_proc_open(struct inode *, struct file *);
|
||||
static int dgrp_gen_proc_close(struct inode *, struct file *);
|
||||
static int parse_write_config(char *);
|
||||
|
||||
|
||||
static const struct file_operations dgrp_proc_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = dgrp_gen_proc_open,
|
||||
.release = dgrp_gen_proc_close,
|
||||
};
|
||||
|
||||
static struct inode_operations proc_inode_ops = {
|
||||
.permission = dgrp_inode_permission
|
||||
};
|
||||
|
||||
|
||||
static void register_proc_table(struct dgrp_proc_entry *,
|
||||
struct proc_dir_entry *);
|
||||
static void unregister_proc_table(struct dgrp_proc_entry *,
|
||||
struct proc_dir_entry *);
|
||||
|
||||
static struct dgrp_proc_entry dgrp_net_table[];
|
||||
static struct dgrp_proc_entry dgrp_mon_table[];
|
||||
static struct dgrp_proc_entry dgrp_ports_table[];
|
||||
static struct dgrp_proc_entry dgrp_dpa_table[];
|
||||
|
||||
static ssize_t config_proc_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *pos);
|
||||
|
||||
static int nodeinfo_proc_open(struct inode *inode, struct file *file);
|
||||
static int info_proc_open(struct inode *inode, struct file *file);
|
||||
static int config_proc_open(struct inode *inode, struct file *file);
|
||||
|
||||
static struct file_operations config_proc_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = config_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
.write = config_proc_write
|
||||
};
|
||||
|
||||
static struct file_operations info_proc_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = info_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static struct file_operations nodeinfo_proc_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = nodeinfo_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static struct dgrp_proc_entry dgrp_table[] = {
|
||||
{
|
||||
.id = DGRP_CONFIG,
|
||||
.name = "config",
|
||||
.mode = 0644,
|
||||
.proc_file_ops = &config_proc_file_ops,
|
||||
},
|
||||
{
|
||||
.id = DGRP_INFO,
|
||||
.name = "info",
|
||||
.mode = 0644,
|
||||
.proc_file_ops = &info_proc_file_ops,
|
||||
},
|
||||
{
|
||||
.id = DGRP_NODEINFO,
|
||||
.name = "nodeinfo",
|
||||
.mode = 0644,
|
||||
.proc_file_ops = &nodeinfo_proc_file_ops,
|
||||
},
|
||||
{
|
||||
.id = DGRP_NETDIR,
|
||||
.name = "net",
|
||||
.mode = 0500,
|
||||
.child = dgrp_net_table
|
||||
},
|
||||
{
|
||||
.id = DGRP_MONDIR,
|
||||
.name = "mon",
|
||||
.mode = 0500,
|
||||
.child = dgrp_mon_table
|
||||
},
|
||||
{
|
||||
.id = DGRP_PORTSDIR,
|
||||
.name = "ports",
|
||||
.mode = 0500,
|
||||
.child = dgrp_ports_table
|
||||
},
|
||||
{
|
||||
.id = DGRP_DPADIR,
|
||||
.name = "dpa",
|
||||
.mode = 0500,
|
||||
.child = dgrp_dpa_table
|
||||
}
|
||||
};
|
||||
|
||||
static struct proc_dir_entry *net_entry_pointer;
|
||||
static struct proc_dir_entry *mon_entry_pointer;
|
||||
static struct proc_dir_entry *dpa_entry_pointer;
|
||||
static struct proc_dir_entry *ports_entry_pointer;
|
||||
|
||||
static struct dgrp_proc_entry dgrp_net_table[] = {
|
||||
{0}
|
||||
};
|
||||
|
||||
static struct dgrp_proc_entry dgrp_mon_table[] = {
|
||||
{0}
|
||||
};
|
||||
|
||||
static struct dgrp_proc_entry dgrp_ports_table[] = {
|
||||
{0}
|
||||
};
|
||||
|
||||
static struct dgrp_proc_entry dgrp_dpa_table[] = {
|
||||
{0}
|
||||
};
|
||||
|
||||
void dgrp_unregister_proc(void)
|
||||
{
|
||||
unregister_proc_table(dgrp_table, dgrp_proc_dir_entry);
|
||||
net_entry_pointer = NULL;
|
||||
mon_entry_pointer = NULL;
|
||||
dpa_entry_pointer = NULL;
|
||||
ports_entry_pointer = NULL;
|
||||
|
||||
if (dgrp_proc_dir_entry) {
|
||||
remove_proc_entry(dgrp_proc_dir_entry->name,
|
||||
dgrp_proc_dir_entry->parent);
|
||||
dgrp_proc_dir_entry = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void dgrp_register_proc(void)
|
||||
{
|
||||
/*
|
||||
* Register /proc/dgrp
|
||||
*/
|
||||
dgrp_proc_dir_entry = proc_create("dgrp", S_IFDIR, NULL,
|
||||
&dgrp_proc_file_ops);
|
||||
register_proc_table(dgrp_table, dgrp_proc_dir_entry);
|
||||
}
|
||||
|
||||
/*
|
||||
* /proc/sys support
|
||||
*/
|
||||
static int dgrp_proc_match(int len, const char *name, struct proc_dir_entry *de)
|
||||
{
|
||||
if (!de || !de->low_ino)
|
||||
return 0;
|
||||
if (de->namelen != len)
|
||||
return 0;
|
||||
return !memcmp(name, de->name, len);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Scan the entries in table and add them all to /proc at the position
|
||||
* referred to by "root"
|
||||
*/
|
||||
static void register_proc_table(struct dgrp_proc_entry *table,
|
||||
struct proc_dir_entry *root)
|
||||
{
|
||||
struct proc_dir_entry *de;
|
||||
int len;
|
||||
mode_t mode;
|
||||
|
||||
for (; table->id; table++) {
|
||||
/* Can't do anything without a proc name. */
|
||||
if (!table->name)
|
||||
continue;
|
||||
|
||||
/* Maybe we can't do anything with it... */
|
||||
if (!table->proc_file_ops &&
|
||||
!table->child) {
|
||||
pr_warn("dgrp: Can't register %s\n",
|
||||
table->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
len = strlen(table->name);
|
||||
mode = table->mode;
|
||||
|
||||
de = NULL;
|
||||
if (!table->child)
|
||||
mode |= S_IFREG;
|
||||
else {
|
||||
mode |= S_IFDIR;
|
||||
for (de = root->subdir; de; de = de->next) {
|
||||
if (dgrp_proc_match(len, table->name, de))
|
||||
break;
|
||||
}
|
||||
/* If the subdir exists already, de is non-NULL */
|
||||
}
|
||||
|
||||
if (!de) {
|
||||
de = create_proc_entry(table->name, mode, root);
|
||||
if (!de)
|
||||
continue;
|
||||
de->data = (void *) table;
|
||||
if (!table->child) {
|
||||
de->proc_iops = &proc_inode_ops;
|
||||
if (table->proc_file_ops)
|
||||
de->proc_fops = table->proc_file_ops;
|
||||
else
|
||||
de->proc_fops = &dgrp_proc_file_ops;
|
||||
}
|
||||
}
|
||||
table->de = de;
|
||||
if (de->mode & S_IFDIR)
|
||||
register_proc_table(table->child, de);
|
||||
|
||||
if (table->id == DGRP_NETDIR)
|
||||
net_entry_pointer = de;
|
||||
|
||||
if (table->id == DGRP_MONDIR)
|
||||
mon_entry_pointer = de;
|
||||
|
||||
if (table->id == DGRP_DPADIR)
|
||||
dpa_entry_pointer = de;
|
||||
|
||||
if (table->id == DGRP_PORTSDIR)
|
||||
ports_entry_pointer = de;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Unregister a /proc sysctl table and any subdirectories.
|
||||
*/
|
||||
static void unregister_proc_table(struct dgrp_proc_entry *table,
|
||||
struct proc_dir_entry *root)
|
||||
{
|
||||
struct proc_dir_entry *de;
|
||||
struct nd_struct *tmp;
|
||||
|
||||
list_for_each_entry(tmp, &nd_struct_list, list) {
|
||||
if ((table == dgrp_net_table) && (tmp->nd_net_de)) {
|
||||
unregister_dgrp_device(tmp->nd_net_de);
|
||||
dgrp_remove_node_class_sysfs_files(tmp);
|
||||
}
|
||||
|
||||
if ((table == dgrp_mon_table) && (tmp->nd_mon_de))
|
||||
unregister_dgrp_device(tmp->nd_mon_de);
|
||||
|
||||
if ((table == dgrp_dpa_table) && (tmp->nd_dpa_de))
|
||||
unregister_dgrp_device(tmp->nd_dpa_de);
|
||||
|
||||
if ((table == dgrp_ports_table) && (tmp->nd_ports_de))
|
||||
unregister_dgrp_device(tmp->nd_ports_de);
|
||||
}
|
||||
|
||||
for (; table->id; table++) {
|
||||
de = table->de;
|
||||
|
||||
if (!de)
|
||||
continue;
|
||||
if (de->mode & S_IFDIR) {
|
||||
if (!table->child) {
|
||||
pr_alert("dgrp: malformed sysctl tree on free\n");
|
||||
continue;
|
||||
}
|
||||
unregister_proc_table(table->child, de);
|
||||
|
||||
/* Don't unregister directories which still have entries */
|
||||
if (de->subdir)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Don't unregister proc entries that are still being used.. */
|
||||
if ((atomic_read(&de->count)) != 1) {
|
||||
pr_alert("proc entry %s in use, not removing\n",
|
||||
de->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
remove_proc_entry(de->name, de->parent);
|
||||
table->de = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dgrp_gen_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct proc_dir_entry *de;
|
||||
struct dgrp_proc_entry *entry;
|
||||
int ret = 0;
|
||||
|
||||
de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode);
|
||||
if (!de || !de->data) {
|
||||
ret = -ENXIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
entry = (struct dgrp_proc_entry *) de->data;
|
||||
if (!entry) {
|
||||
ret = -ENXIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
down(&entry->excl_sem);
|
||||
|
||||
if (entry->excl_cnt)
|
||||
ret = -EBUSY;
|
||||
else
|
||||
entry->excl_cnt++;
|
||||
|
||||
up(&entry->excl_sem);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dgrp_gen_proc_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct proc_dir_entry *de;
|
||||
struct dgrp_proc_entry *entry;
|
||||
|
||||
de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode);
|
||||
if (!de || !de->data)
|
||||
goto done;
|
||||
|
||||
entry = (struct dgrp_proc_entry *) de->data;
|
||||
if (!entry)
|
||||
goto done;
|
||||
|
||||
down(&entry->excl_sem);
|
||||
|
||||
if (entry->excl_cnt)
|
||||
entry->excl_cnt = 0;
|
||||
|
||||
up(&entry->excl_sem);
|
||||
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *config_proc_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
return seq_list_start_head(&nd_struct_list, *pos);
|
||||
}
|
||||
|
||||
static void *config_proc_next(struct seq_file *p, void *v, loff_t *pos)
|
||||
{
|
||||
return seq_list_next(v, &nd_struct_list, pos);
|
||||
}
|
||||
|
||||
static void config_proc_stop(struct seq_file *m, void *v)
|
||||
{
|
||||
}
|
||||
|
||||
static int config_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
char tmp_id[4];
|
||||
|
||||
if (v == &nd_struct_list) {
|
||||
seq_puts(m, "#-----------------------------------------------------------------------------\n");
|
||||
seq_puts(m, "# Avail\n");
|
||||
seq_puts(m, "# ID Major State Ports\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
nd = list_entry(v, struct nd_struct, list);
|
||||
|
||||
ID_TO_CHAR(nd->nd_ID, tmp_id);
|
||||
|
||||
seq_printf(m, " %-2.2s %-5ld %-10.10s %-5d\n",
|
||||
tmp_id,
|
||||
nd->nd_major,
|
||||
ND_STATE_STR(nd->nd_state),
|
||||
nd->nd_chan_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations proc_config_ops = {
|
||||
.start = config_proc_start,
|
||||
.next = config_proc_next,
|
||||
.stop = config_proc_stop,
|
||||
.show = config_proc_show
|
||||
};
|
||||
|
||||
static int config_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &proc_config_ops);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* When writing configuration information, each "record" (i.e. each
|
||||
* write) is treated as an independent request. See the "parse"
|
||||
* description for more details.
|
||||
*/
|
||||
static ssize_t config_proc_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
ssize_t retval;
|
||||
char *inbuf, *sp;
|
||||
char *line, *ldelim;
|
||||
|
||||
if (count > 32768)
|
||||
return -EINVAL;
|
||||
|
||||
inbuf = sp = vzalloc(count + 1);
|
||||
if (!inbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(inbuf, buffer, count)) {
|
||||
retval = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
inbuf[count] = 0;
|
||||
|
||||
ldelim = "\n";
|
||||
|
||||
line = strpbrk(sp, ldelim);
|
||||
while (line) {
|
||||
*line = 0;
|
||||
retval = parse_write_config(sp);
|
||||
if (retval)
|
||||
goto done;
|
||||
|
||||
sp = line + 1;
|
||||
line = strpbrk(sp, ldelim);
|
||||
}
|
||||
|
||||
retval = count;
|
||||
done:
|
||||
vfree(inbuf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------------------
|
||||
*
|
||||
* The following are the functions to parse input
|
||||
*
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
static inline char *skip_past_ws(const char *str)
|
||||
{
|
||||
while ((*str) && !isspace(*str))
|
||||
++str;
|
||||
|
||||
return skip_spaces(str);
|
||||
}
|
||||
|
||||
static int parse_id(char **c, char *cID)
|
||||
{
|
||||
int tmp = **c;
|
||||
|
||||
if (isalnum(tmp) || (tmp == '_'))
|
||||
cID[0] = tmp;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
(*c)++; tmp = **c;
|
||||
|
||||
if (isalnum(tmp) || (tmp == '_')) {
|
||||
cID[1] = tmp;
|
||||
(*c)++;
|
||||
} else
|
||||
cID[1] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_add_config(char *buf)
|
||||
{
|
||||
char *c = buf;
|
||||
int retval;
|
||||
char cID[2];
|
||||
long ID;
|
||||
|
||||
c = skip_past_ws(c);
|
||||
|
||||
retval = parse_id(&c, cID);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
ID = CHAR_TO_ID(cID);
|
||||
|
||||
c = skip_past_ws(c);
|
||||
|
||||
return dgrp_add_id(ID);
|
||||
}
|
||||
|
||||
static int parse_del_config(char *buf)
|
||||
{
|
||||
char *c = buf;
|
||||
int retval;
|
||||
struct nd_struct *nd;
|
||||
char cID[2];
|
||||
long ID;
|
||||
long major;
|
||||
|
||||
c = skip_past_ws(c);
|
||||
|
||||
retval = parse_id(&c, cID);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
ID = CHAR_TO_ID(cID);
|
||||
|
||||
c = skip_past_ws(c);
|
||||
|
||||
retval = kstrtol(c, 10, &major);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
nd = nd_struct_get(major);
|
||||
if (!nd)
|
||||
return -EINVAL;
|
||||
|
||||
if ((nd->nd_major != major) || (nd->nd_ID != ID))
|
||||
return -EINVAL;
|
||||
|
||||
return dgrp_remove_nd(nd);
|
||||
}
|
||||
|
||||
static int parse_chg_config(char *buf)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The passed character buffer represents a single configuration request.
|
||||
* If the first character is a "+", it is parsed as a request to add a
|
||||
* PortServer
|
||||
* If the first character is a "-", it is parsed as a request to delete a
|
||||
* PortServer
|
||||
* If the first character is a "*", it is parsed as a request to change a
|
||||
* PortServer
|
||||
* Any other character (including whitespace) causes the record to be
|
||||
* ignored.
|
||||
*/
|
||||
static int parse_write_config(char *buf)
|
||||
{
|
||||
int retval;
|
||||
|
||||
switch (buf[0]) {
|
||||
case '+':
|
||||
retval = parse_add_config(buf);
|
||||
break;
|
||||
case '-':
|
||||
retval = parse_del_config(buf);
|
||||
break;
|
||||
case '*':
|
||||
retval = parse_chg_config(buf);
|
||||
break;
|
||||
default:
|
||||
retval = -EINVAL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int info_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
seq_printf(m, "version: %s\n", DIGI_VERSION);
|
||||
seq_puts(m, "register_with_sysfs: 1\n");
|
||||
seq_printf(m, "rawreadok: 0x%08x\t(%d)\n",
|
||||
dgrp_rawreadok, dgrp_rawreadok);
|
||||
seq_printf(m, "pollrate: 0x%08x\t(%d)\n",
|
||||
dgrp_poll_tick, dgrp_poll_tick);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int info_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, info_proc_show, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void *nodeinfo_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
return seq_list_start_head(&nd_struct_list, *pos);
|
||||
}
|
||||
|
||||
static void *nodeinfo_next(struct seq_file *p, void *v, loff_t *pos)
|
||||
{
|
||||
return seq_list_next(v, &nd_struct_list, pos);
|
||||
}
|
||||
|
||||
static void nodeinfo_stop(struct seq_file *m, void *v)
|
||||
{
|
||||
}
|
||||
|
||||
static int nodeinfo_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
char hwver[8];
|
||||
char swver[8];
|
||||
char tmp_id[4];
|
||||
|
||||
if (v == &nd_struct_list) {
|
||||
seq_puts(m, "#-----------------------------------------------------------------------------\n");
|
||||
seq_puts(m, "# HW HW SW\n");
|
||||
seq_puts(m, "# ID State Version ID Version Description\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
nd = list_entry(v, struct nd_struct, list);
|
||||
|
||||
ID_TO_CHAR(nd->nd_ID, tmp_id);
|
||||
|
||||
if (nd->nd_state == NS_READY) {
|
||||
sprintf(hwver, "%d.%d", (nd->nd_hw_ver >> 8) & 0xff,
|
||||
nd->nd_hw_ver & 0xff);
|
||||
sprintf(swver, "%d.%d", (nd->nd_sw_ver >> 8) & 0xff,
|
||||
nd->nd_sw_ver & 0xff);
|
||||
seq_printf(m, " %-2.2s %-10.10s %-7.7s %-3d %-7.7s %-35.35s\n",
|
||||
tmp_id,
|
||||
ND_STATE_STR(nd->nd_state),
|
||||
hwver,
|
||||
nd->nd_hw_id,
|
||||
swver,
|
||||
nd->nd_ps_desc);
|
||||
|
||||
} else {
|
||||
seq_printf(m, " %-2.2s %-10.10s\n",
|
||||
tmp_id,
|
||||
ND_STATE_STR(nd->nd_state));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct seq_operations nodeinfo_ops = {
|
||||
.start = nodeinfo_start,
|
||||
.next = nodeinfo_next,
|
||||
.stop = nodeinfo_stop,
|
||||
.show = nodeinfo_show
|
||||
};
|
||||
|
||||
static int nodeinfo_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &nodeinfo_ops);
|
||||
}
|
||||
|
||||
/**
|
||||
* dgrp_add_id() -- creates new nd struct and adds it to list
|
||||
* @id: id of device to add
|
||||
*/
|
||||
static int dgrp_add_id(long id)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
nd = kzalloc(sizeof(struct nd_struct), GFP_KERNEL);
|
||||
if (!nd)
|
||||
return -ENOMEM;
|
||||
|
||||
nd->nd_major = 0;
|
||||
nd->nd_ID = id;
|
||||
|
||||
spin_lock_init(&nd->nd_lock);
|
||||
|
||||
init_waitqueue_head(&nd->nd_tx_waitq);
|
||||
init_waitqueue_head(&nd->nd_mon_wqueue);
|
||||
init_waitqueue_head(&nd->nd_dpa_wqueue);
|
||||
for (i = 0; i < SEQ_MAX; i++)
|
||||
init_waitqueue_head(&nd->nd_seq_wque[i]);
|
||||
|
||||
/* setup the structures to get the major number */
|
||||
ret = dgrp_tty_init(nd);
|
||||
if (ret)
|
||||
goto error_out;
|
||||
|
||||
nd->nd_major = nd->nd_serial_ttdriver->major;
|
||||
|
||||
ret = nd_struct_add(nd);
|
||||
if (ret)
|
||||
goto error_out;
|
||||
|
||||
register_dgrp_device(nd, net_entry_pointer, dgrp_register_net_hook);
|
||||
register_dgrp_device(nd, mon_entry_pointer, dgrp_register_mon_hook);
|
||||
register_dgrp_device(nd, dpa_entry_pointer, dgrp_register_dpa_hook);
|
||||
register_dgrp_device(nd, ports_entry_pointer,
|
||||
dgrp_register_ports_hook);
|
||||
|
||||
return 0;
|
||||
|
||||
error_out:
|
||||
kfree(nd);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int dgrp_remove_nd(struct nd_struct *nd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Check to see if the selected structure is in use */
|
||||
if (nd->nd_tty_ref_cnt)
|
||||
return -EBUSY;
|
||||
|
||||
if (nd->nd_net_de) {
|
||||
unregister_dgrp_device(nd->nd_net_de);
|
||||
dgrp_remove_node_class_sysfs_files(nd);
|
||||
}
|
||||
|
||||
if (nd->nd_mon_de)
|
||||
unregister_dgrp_device(nd->nd_mon_de);
|
||||
|
||||
if (nd->nd_ports_de)
|
||||
unregister_dgrp_device(nd->nd_ports_de);
|
||||
|
||||
if (nd->nd_dpa_de)
|
||||
unregister_dgrp_device(nd->nd_dpa_de);
|
||||
|
||||
dgrp_tty_uninit(nd);
|
||||
|
||||
ret = nd_struct_del(nd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kfree(nd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_dgrp_device(struct nd_struct *node,
|
||||
struct proc_dir_entry *root,
|
||||
void (*register_hook)(struct proc_dir_entry *de))
|
||||
{
|
||||
char buf[3];
|
||||
struct proc_dir_entry *de;
|
||||
|
||||
ID_TO_CHAR(node->nd_ID, buf);
|
||||
|
||||
de = create_proc_entry(buf, 0600 | S_IFREG, root);
|
||||
if (!de)
|
||||
return;
|
||||
|
||||
de->data = (void *) node;
|
||||
|
||||
if (register_hook)
|
||||
register_hook(de);
|
||||
|
||||
}
|
||||
|
||||
static void unregister_dgrp_device(struct proc_dir_entry *de)
|
||||
{
|
||||
if (!de)
|
||||
return;
|
||||
|
||||
/* Don't unregister proc entries that are still being used.. */
|
||||
if ((atomic_read(&de->count)) != 1) {
|
||||
pr_alert("%s - proc entry %s in use. Not removing.\n",
|
||||
__func__, de->name);
|
||||
return;
|
||||
}
|
||||
|
||||
remove_proc_entry(de->name, de->parent);
|
||||
de = NULL;
|
||||
}
|
|
@ -0,0 +1,555 @@
|
|||
/*
|
||||
* Copyright 2004 Digi International (www.digi.com)
|
||||
* Scott H Kilau <Scott_Kilau 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "dgrp_common.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kdev_t.h>
|
||||
|
||||
|
||||
#define PORTSERVER_DIVIDEND 1843200
|
||||
#define SERIAL_TYPE_NORMAL 1
|
||||
#define SERIAL_TYPE_CALLOUT 2
|
||||
#define SERIAL_TYPE_XPRINT 3
|
||||
|
||||
|
||||
static struct class *dgrp_class;
|
||||
static struct device *dgrp_class_nodes_dev;
|
||||
static struct device *dgrp_class_global_settings_dev;
|
||||
|
||||
|
||||
static ssize_t dgrp_class_version_show(struct class *class,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", DIGI_VERSION);
|
||||
}
|
||||
static CLASS_ATTR(driver_version, 0400, dgrp_class_version_show, NULL);
|
||||
|
||||
|
||||
static ssize_t dgrp_class_register_with_sysfs_show(struct device *c,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "1\n");
|
||||
}
|
||||
static DEVICE_ATTR(register_with_sysfs, 0400,
|
||||
dgrp_class_register_with_sysfs_show, NULL);
|
||||
|
||||
|
||||
static ssize_t dgrp_class_rawreadok_show(struct device *c,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", dgrp_rawreadok);
|
||||
}
|
||||
static ssize_t dgrp_class_rawreadok_store(struct device *c,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
sscanf(buf, "0x%x\n", &dgrp_rawreadok);
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(rawreadok, 0600, dgrp_class_rawreadok_show,
|
||||
dgrp_class_rawreadok_store);
|
||||
|
||||
|
||||
static ssize_t dgrp_class_pollrate_show(struct device *c,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", dgrp_poll_tick);
|
||||
}
|
||||
|
||||
static ssize_t dgrp_class_pollrate_store(struct device *c,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
sscanf(buf, "0x%x\n", &dgrp_poll_tick);
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(pollrate, 0600, dgrp_class_pollrate_show,
|
||||
dgrp_class_pollrate_store);
|
||||
|
||||
static struct attribute *dgrp_sysfs_global_settings_entries[] = {
|
||||
&dev_attr_pollrate.attr,
|
||||
&dev_attr_rawreadok.attr,
|
||||
&dev_attr_register_with_sysfs.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static struct attribute_group dgrp_global_settings_attribute_group = {
|
||||
.name = NULL,
|
||||
.attrs = dgrp_sysfs_global_settings_entries,
|
||||
};
|
||||
|
||||
|
||||
|
||||
void dgrp_create_class_sysfs_files(void)
|
||||
{
|
||||
int ret = 0;
|
||||
int max_majors = 1U << (32 - MINORBITS);
|
||||
|
||||
dgrp_class = class_create(THIS_MODULE, "digi_realport");
|
||||
ret = class_create_file(dgrp_class, &class_attr_driver_version);
|
||||
|
||||
dgrp_class_global_settings_dev = device_create(dgrp_class, NULL,
|
||||
MKDEV(0, max_majors + 1), NULL, "driver_settings");
|
||||
|
||||
ret = sysfs_create_group(&dgrp_class_global_settings_dev->kobj,
|
||||
&dgrp_global_settings_attribute_group);
|
||||
if (ret) {
|
||||
pr_alert("%s: failed to create sysfs global settings device attributes.\n",
|
||||
__func__);
|
||||
sysfs_remove_group(&dgrp_class_global_settings_dev->kobj,
|
||||
&dgrp_global_settings_attribute_group);
|
||||
return;
|
||||
}
|
||||
|
||||
dgrp_class_nodes_dev = device_create(dgrp_class, NULL,
|
||||
MKDEV(0, max_majors + 2), NULL, "nodes");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void dgrp_remove_class_sysfs_files(void)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
int max_majors = 1U << (32 - MINORBITS);
|
||||
|
||||
list_for_each_entry(nd, &nd_struct_list, list)
|
||||
dgrp_remove_node_class_sysfs_files(nd);
|
||||
|
||||
sysfs_remove_group(&dgrp_class_global_settings_dev->kobj,
|
||||
&dgrp_global_settings_attribute_group);
|
||||
|
||||
class_remove_file(dgrp_class, &class_attr_driver_version);
|
||||
|
||||
device_destroy(dgrp_class, MKDEV(0, max_majors + 1));
|
||||
device_destroy(dgrp_class, MKDEV(0, max_majors + 2));
|
||||
class_destroy(dgrp_class);
|
||||
}
|
||||
|
||||
static ssize_t dgrp_node_state_show(struct device *c,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
nd = (struct nd_struct *) dev_get_drvdata(c);
|
||||
if (!nd)
|
||||
return 0;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", ND_STATE_STR(nd->nd_state));
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(state, 0600, dgrp_node_state_show, NULL);
|
||||
|
||||
static ssize_t dgrp_node_description_show(struct device *c,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
nd = (struct nd_struct *) dev_get_drvdata(c);
|
||||
if (!nd)
|
||||
return 0;
|
||||
|
||||
if (nd->nd_state == NS_READY && nd->nd_ps_desc)
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", nd->nd_ps_desc);
|
||||
return 0;
|
||||
}
|
||||
static DEVICE_ATTR(description_info, 0600, dgrp_node_description_show, NULL);
|
||||
|
||||
static ssize_t dgrp_node_hw_version_show(struct device *c,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
nd = (struct nd_struct *) dev_get_drvdata(c);
|
||||
if (!nd)
|
||||
return 0;
|
||||
|
||||
if (nd->nd_state == NS_READY)
|
||||
return snprintf(buf, PAGE_SIZE, "%d.%d\n",
|
||||
(nd->nd_hw_ver >> 8) & 0xff,
|
||||
nd->nd_hw_ver & 0xff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static DEVICE_ATTR(hw_version_info, 0600, dgrp_node_hw_version_show, NULL);
|
||||
|
||||
static ssize_t dgrp_node_hw_id_show(struct device *c,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
nd = (struct nd_struct *) dev_get_drvdata(c);
|
||||
if (!nd)
|
||||
return 0;
|
||||
|
||||
|
||||
if (nd->nd_state == NS_READY)
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", nd->nd_hw_id);
|
||||
return 0;
|
||||
}
|
||||
static DEVICE_ATTR(hw_id_info, 0600, dgrp_node_hw_id_show, NULL);
|
||||
|
||||
static ssize_t dgrp_node_sw_version_show(struct device *c,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
nd = (struct nd_struct *) dev_get_drvdata(c);
|
||||
if (!nd)
|
||||
return 0;
|
||||
|
||||
if (nd->nd_state == NS_READY)
|
||||
return snprintf(buf, PAGE_SIZE, "%d.%d\n",
|
||||
(nd->nd_sw_ver >> 8) & 0xff,
|
||||
nd->nd_sw_ver & 0xff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static DEVICE_ATTR(sw_version_info, 0600, dgrp_node_sw_version_show, NULL);
|
||||
|
||||
|
||||
static struct attribute *dgrp_sysfs_node_entries[] = {
|
||||
&dev_attr_state.attr,
|
||||
&dev_attr_description_info.attr,
|
||||
&dev_attr_hw_version_info.attr,
|
||||
&dev_attr_hw_id_info.attr,
|
||||
&dev_attr_sw_version_info.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static struct attribute_group dgrp_node_attribute_group = {
|
||||
.name = NULL,
|
||||
.attrs = dgrp_sysfs_node_entries,
|
||||
};
|
||||
|
||||
|
||||
void dgrp_create_node_class_sysfs_files(struct nd_struct *nd)
|
||||
{
|
||||
int ret;
|
||||
char name[10];
|
||||
|
||||
if (nd->nd_ID)
|
||||
ID_TO_CHAR(nd->nd_ID, name);
|
||||
else
|
||||
sprintf(name, "node%ld", nd->nd_major);
|
||||
|
||||
nd->nd_class_dev = device_create(dgrp_class, dgrp_class_nodes_dev,
|
||||
MKDEV(0, nd->nd_major), NULL, name);
|
||||
|
||||
ret = sysfs_create_group(&nd->nd_class_dev->kobj,
|
||||
&dgrp_node_attribute_group);
|
||||
|
||||
if (ret) {
|
||||
pr_alert("%s: failed to create sysfs node device attributes.\n",
|
||||
__func__);
|
||||
sysfs_remove_group(&nd->nd_class_dev->kobj,
|
||||
&dgrp_node_attribute_group);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_set_drvdata(nd->nd_class_dev, nd);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void dgrp_remove_node_class_sysfs_files(struct nd_struct *nd)
|
||||
{
|
||||
if (nd->nd_class_dev) {
|
||||
sysfs_remove_group(&nd->nd_class_dev->kobj,
|
||||
&dgrp_node_attribute_group);
|
||||
|
||||
device_destroy(dgrp_class, MKDEV(0, nd->nd_major));
|
||||
nd->nd_class_dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ssize_t dgrp_tty_state_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct un_struct *un;
|
||||
|
||||
if (!d)
|
||||
return 0;
|
||||
un = (struct un_struct *) dev_get_drvdata(d);
|
||||
if (!un)
|
||||
return 0;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
un->un_open_count ? "Open" : "Closed");
|
||||
}
|
||||
static DEVICE_ATTR(state_info, 0600, dgrp_tty_state_show, NULL);
|
||||
|
||||
static ssize_t dgrp_tty_baud_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ch_struct *ch;
|
||||
struct un_struct *un;
|
||||
|
||||
if (!d)
|
||||
return 0;
|
||||
un = (struct un_struct *) dev_get_drvdata(d);
|
||||
if (!un)
|
||||
return 0;
|
||||
ch = un->un_ch;
|
||||
if (!ch)
|
||||
return 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
un->un_open_count ? (PORTSERVER_DIVIDEND / ch->ch_s_brate) : 0);
|
||||
}
|
||||
static DEVICE_ATTR(baud_info, 0400, dgrp_tty_baud_show, NULL);
|
||||
|
||||
|
||||
static ssize_t dgrp_tty_msignals_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ch_struct *ch;
|
||||
struct un_struct *un;
|
||||
|
||||
if (!d)
|
||||
return 0;
|
||||
un = (struct un_struct *) dev_get_drvdata(d);
|
||||
if (!un)
|
||||
return 0;
|
||||
ch = un->un_ch;
|
||||
if (!ch)
|
||||
return 0;
|
||||
|
||||
if (ch->ch_open_count) {
|
||||
return snprintf(buf, PAGE_SIZE, "%s %s %s %s %s %s\n",
|
||||
(ch->ch_s_mlast & DM_RTS) ? "RTS" : "",
|
||||
(ch->ch_s_mlast & DM_CTS) ? "CTS" : "",
|
||||
(ch->ch_s_mlast & DM_DTR) ? "DTR" : "",
|
||||
(ch->ch_s_mlast & DM_DSR) ? "DSR" : "",
|
||||
(ch->ch_s_mlast & DM_CD) ? "DCD" : "",
|
||||
(ch->ch_s_mlast & DM_RI) ? "RI" : "");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static DEVICE_ATTR(msignals_info, 0400, dgrp_tty_msignals_show, NULL);
|
||||
|
||||
|
||||
static ssize_t dgrp_tty_iflag_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ch_struct *ch;
|
||||
struct un_struct *un;
|
||||
|
||||
if (!d)
|
||||
return 0;
|
||||
un = (struct un_struct *) dev_get_drvdata(d);
|
||||
if (!un)
|
||||
return 0;
|
||||
ch = un->un_ch;
|
||||
if (!ch)
|
||||
return 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_s_iflag);
|
||||
}
|
||||
static DEVICE_ATTR(iflag_info, 0600, dgrp_tty_iflag_show, NULL);
|
||||
|
||||
|
||||
static ssize_t dgrp_tty_cflag_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ch_struct *ch;
|
||||
struct un_struct *un;
|
||||
|
||||
if (!d)
|
||||
return 0;
|
||||
un = (struct un_struct *) dev_get_drvdata(d);
|
||||
if (!un)
|
||||
return 0;
|
||||
ch = un->un_ch;
|
||||
if (!ch)
|
||||
return 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_s_cflag);
|
||||
}
|
||||
static DEVICE_ATTR(cflag_info, 0600, dgrp_tty_cflag_show, NULL);
|
||||
|
||||
|
||||
static ssize_t dgrp_tty_oflag_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ch_struct *ch;
|
||||
struct un_struct *un;
|
||||
|
||||
if (!d)
|
||||
return 0;
|
||||
un = (struct un_struct *) dev_get_drvdata(d);
|
||||
if (!un)
|
||||
return 0;
|
||||
ch = un->un_ch;
|
||||
if (!ch)
|
||||
return 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_s_oflag);
|
||||
}
|
||||
static DEVICE_ATTR(oflag_info, 0600, dgrp_tty_oflag_show, NULL);
|
||||
|
||||
|
||||
static ssize_t dgrp_tty_digi_flag_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ch_struct *ch;
|
||||
struct un_struct *un;
|
||||
|
||||
if (!d)
|
||||
return 0;
|
||||
un = (struct un_struct *) dev_get_drvdata(d);
|
||||
if (!un)
|
||||
return 0;
|
||||
ch = un->un_ch;
|
||||
if (!ch)
|
||||
return 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_digi.digi_flags);
|
||||
}
|
||||
static DEVICE_ATTR(digi_flag_info, 0600, dgrp_tty_digi_flag_show, NULL);
|
||||
|
||||
|
||||
static ssize_t dgrp_tty_rxcount_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ch_struct *ch;
|
||||
struct un_struct *un;
|
||||
|
||||
if (!d)
|
||||
return 0;
|
||||
un = (struct un_struct *) dev_get_drvdata(d);
|
||||
if (!un)
|
||||
return 0;
|
||||
ch = un->un_ch;
|
||||
if (!ch)
|
||||
return 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_rxcount);
|
||||
}
|
||||
static DEVICE_ATTR(rxcount_info, 0600, dgrp_tty_rxcount_show, NULL);
|
||||
|
||||
|
||||
static ssize_t dgrp_tty_txcount_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ch_struct *ch;
|
||||
struct un_struct *un;
|
||||
|
||||
if (!d)
|
||||
return 0;
|
||||
un = (struct un_struct *) dev_get_drvdata(d);
|
||||
if (!un)
|
||||
return 0;
|
||||
ch = un->un_ch;
|
||||
if (!ch)
|
||||
return 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_txcount);
|
||||
}
|
||||
static DEVICE_ATTR(txcount_info, 0600, dgrp_tty_txcount_show, NULL);
|
||||
|
||||
|
||||
static ssize_t dgrp_tty_name_show(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nd_struct *nd;
|
||||
struct ch_struct *ch;
|
||||
struct un_struct *un;
|
||||
char name[10];
|
||||
|
||||
if (!d)
|
||||
return 0;
|
||||
un = (struct un_struct *) dev_get_drvdata(d);
|
||||
if (!un)
|
||||
return 0;
|
||||
ch = un->un_ch;
|
||||
if (!ch)
|
||||
return 0;
|
||||
nd = ch->ch_nd;
|
||||
if (!nd)
|
||||
return 0;
|
||||
|
||||
ID_TO_CHAR(nd->nd_ID, name);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s%s%02d\n",
|
||||
un->un_type == SERIAL_TYPE_XPRINT ? "pr" : "tty",
|
||||
name, ch->ch_portnum);
|
||||
}
|
||||
static DEVICE_ATTR(custom_name, 0600, dgrp_tty_name_show, NULL);
|
||||
|
||||
|
||||
static struct attribute *dgrp_sysfs_tty_entries[] = {
|
||||
&dev_attr_state_info.attr,
|
||||
&dev_attr_baud_info.attr,
|
||||
&dev_attr_msignals_info.attr,
|
||||
&dev_attr_iflag_info.attr,
|
||||
&dev_attr_cflag_info.attr,
|
||||
&dev_attr_oflag_info.attr,
|
||||
&dev_attr_digi_flag_info.attr,
|
||||
&dev_attr_rxcount_info.attr,
|
||||
&dev_attr_txcount_info.attr,
|
||||
&dev_attr_custom_name.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static struct attribute_group dgrp_tty_attribute_group = {
|
||||
.name = NULL,
|
||||
.attrs = dgrp_sysfs_tty_entries,
|
||||
};
|
||||
|
||||
|
||||
void dgrp_create_tty_sysfs(struct un_struct *un, struct device *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sysfs_create_group(&c->kobj, &dgrp_tty_attribute_group);
|
||||
if (ret) {
|
||||
pr_alert("%s: failed to create sysfs tty device attributes.\n",
|
||||
__func__);
|
||||
sysfs_remove_group(&c->kobj, &dgrp_tty_attribute_group);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_set_drvdata(c, un);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void dgrp_remove_tty_sysfs(struct device *c)
|
||||
{
|
||||
sysfs_remove_group(&c->kobj, &dgrp_tty_attribute_group);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,129 @@
|
|||
/************************************************************************
|
||||
* HP-UX Realport Daemon interface file.
|
||||
*
|
||||
* Copyright (C) 1998, by Digi International. All Rights Reserved.
|
||||
************************************************************************/
|
||||
|
||||
#ifndef _DIGIDRP_H
|
||||
#define _DIGIDRP_H
|
||||
|
||||
/************************************************************************
|
||||
* This file contains defines for the ioctl() interface to
|
||||
* the realport driver. This ioctl() interface is used by the
|
||||
* daemon to set speed setup parameters honored by the driver.
|
||||
************************************************************************/
|
||||
|
||||
struct link_struct {
|
||||
int lk_fast_rate; /* Fast line rate to be used
|
||||
when the delay is less-equal
|
||||
to lk_fast_delay */
|
||||
|
||||
int lk_fast_delay; /* Fast line rate delay in
|
||||
milliseconds */
|
||||
|
||||
int lk_slow_rate; /* Slow line rate to be used when
|
||||
the delay is greater-equal
|
||||
to lk_slow_delay */
|
||||
|
||||
int lk_slow_delay; /* Slow line rate delay in
|
||||
milliseconds */
|
||||
|
||||
int lk_header_size; /* Estimated packet header size
|
||||
when sent across the slowest
|
||||
link. */
|
||||
};
|
||||
|
||||
#define DIGI_GETLINK _IOW('e', 103, struct link_struct) /* Get link parameters */
|
||||
#define DIGI_SETLINK _IOW('e', 104, struct link_struct) /* Set link parameters */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* This module provides application access to special Digi
|
||||
* serial line enhancements which are not standard UNIX(tm) features.
|
||||
************************************************************************/
|
||||
|
||||
struct digiflow_struct {
|
||||
unsigned char startc; /* flow cntl start char */
|
||||
unsigned char stopc; /* flow cntl stop char */
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* Values for digi_flags
|
||||
************************************************************************/
|
||||
#define DIGI_IXON 0x0001 /* Handle IXON in the FEP */
|
||||
#define DIGI_FAST 0x0002 /* Fast baud rates */
|
||||
#define RTSPACE 0x0004 /* RTS input flow control */
|
||||
#define CTSPACE 0x0008 /* CTS output flow control */
|
||||
#define DSRPACE 0x0010 /* DSR output flow control */
|
||||
#define DCDPACE 0x0020 /* DCD output flow control */
|
||||
#define DTRPACE 0x0040 /* DTR input flow control */
|
||||
#define DIGI_COOK 0x0080 /* Cooked processing done in FEP */
|
||||
#define DIGI_FORCEDCD 0x0100 /* Force carrier */
|
||||
#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */
|
||||
#define DIGI_AIXON 0x0400 /* Aux flow control in fep */
|
||||
#define DIGI_PRINTER 0x0800 /* Hold port open for flow cntrl */
|
||||
#define DIGI_PP_INPUT 0x1000 /* Change parallel port to input */
|
||||
#define DIGI_422 0x4000 /* Change parallel port to input */
|
||||
#define DIGI_RTS_TOGGLE 0x8000 /* Support RTS Toggle */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Values associated with transparent print
|
||||
************************************************************************/
|
||||
#define DIGI_PLEN 8 /* String length */
|
||||
#define DIGI_TSIZ 10 /* Terminal string len */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Structure used with ioctl commands for DIGI parameters.
|
||||
************************************************************************/
|
||||
struct digi_struct {
|
||||
unsigned short digi_flags; /* Flags (see above) */
|
||||
unsigned short digi_maxcps; /* Max printer CPS */
|
||||
unsigned short digi_maxchar; /* Max chars in print queue */
|
||||
unsigned short digi_bufsize; /* Buffer size */
|
||||
unsigned char digi_onlen; /* Length of ON string */
|
||||
unsigned char digi_offlen; /* Length of OFF string */
|
||||
char digi_onstr[DIGI_PLEN]; /* Printer on string */
|
||||
char digi_offstr[DIGI_PLEN]; /* Printer off string */
|
||||
char digi_term[DIGI_TSIZ]; /* terminal string */
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* Ioctl command arguments for DIGI parameters.
|
||||
************************************************************************/
|
||||
/* Read params */
|
||||
#define DIGI_GETA _IOR('e', 94, struct digi_struct)
|
||||
|
||||
/* Set params */
|
||||
#define DIGI_SETA _IOW('e', 95, struct digi_struct)
|
||||
|
||||
/* Drain & set params */
|
||||
#define DIGI_SETAW _IOW('e', 96, struct digi_struct)
|
||||
|
||||
/* Drain, flush & set params */
|
||||
#define DIGI_SETAF _IOW('e', 97, struct digi_struct)
|
||||
|
||||
/* Get startc/stopc flow control characters */
|
||||
#define DIGI_GETFLOW _IOR('e', 99, struct digiflow_struct)
|
||||
|
||||
/* Set startc/stopc flow control characters */
|
||||
#define DIGI_SETFLOW _IOW('e', 100, struct digiflow_struct)
|
||||
|
||||
/* Get Aux. startc/stopc flow control chars */
|
||||
#define DIGI_GETAFLOW _IOR('e', 101, struct digiflow_struct)
|
||||
|
||||
/* Set Aux. startc/stopc flow control chars */
|
||||
#define DIGI_SETAFLOW _IOW('e', 102, struct digiflow_struct)
|
||||
|
||||
/* Set integer baud rate */
|
||||
#define DIGI_SETCUSTOMBAUD _IOW('e', 106, int)
|
||||
|
||||
/* Get integer baud rate */
|
||||
#define DIGI_GETCUSTOMBAUD _IOR('e', 107, int)
|
||||
|
||||
#define DIGI_GEDELAY _IOR('d', 246, int) /* Get edelay */
|
||||
#define DIGI_SEDELAY _IOW('d', 247, int) /* Get edelay */
|
||||
|
||||
|
||||
#endif /* _DIGIDRP_H */
|
|
@ -0,0 +1,693 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 1999 Digi International (www.digi.com)
|
||||
* Gene Olson <gene at digi dot com>
|
||||
* James Puzzo <jamesp at digi dot com>
|
||||
* Scott Kilau <scottk at digi dot com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/************************************************************************
|
||||
* Master include file for Linux Realport Driver.
|
||||
************************************************************************/
|
||||
|
||||
#ifndef __DRP_H
|
||||
#define __DRP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/tty.h>
|
||||
|
||||
|
||||
#include "digirp.h"
|
||||
|
||||
/************************************************************************
|
||||
* Tuning parameters.
|
||||
************************************************************************/
|
||||
|
||||
#define CHAN_MAX 64 /* Max # ports per server */
|
||||
|
||||
#define SEQ_MAX 128 /* Max # transmit sequences (2^n) */
|
||||
#define SEQ_MASK (SEQ_MAX-1) /* Sequence buffer modulus mask */
|
||||
|
||||
#define TBUF_MAX 4096 /* Size of transmit buffer (2^n) */
|
||||
#define RBUF_MAX 4096 /* Size of receive buffer (2^n) */
|
||||
|
||||
#define TBUF_MASK (TBUF_MAX-1) /* Transmit buffer modulus mask */
|
||||
#define RBUF_MASK (RBUF_MAX-1) /* Receive buffer modulus mask */
|
||||
|
||||
#define TBUF_LOW 1000 /* Transmit low water mark */
|
||||
|
||||
#define UIO_BASE 1000 /* Base for write operations */
|
||||
#define UIO_MIN 2000 /* Minimum size application buffer */
|
||||
#define UIO_MAX 8100 /* Unix I/O buffer size */
|
||||
|
||||
#define MON_MAX 65536 /* Monitor buffer size (2^n) */
|
||||
#define MON_MASK (MON_MAX-1) /* Monitor wrap mask */
|
||||
|
||||
#define DPA_MAX 65536 /* DPA buffer size (2^n) */
|
||||
#define DPA_MASK (DPA_MAX-1) /* DPA wrap mask */
|
||||
#define DPA_HIGH_WATER 58000 /* Enforce flow control when
|
||||
* over this amount
|
||||
*/
|
||||
|
||||
#define IDLE_MAX (20 * HZ) /* Max TCP link idle time */
|
||||
|
||||
#define MAX_DESC_LEN 100 /* Maximum length of stored PS
|
||||
* description
|
||||
*/
|
||||
|
||||
#define WRITEBUFLEN ((4096) + 4) /* 4 extra for alignment play space */
|
||||
|
||||
#define VPDSIZE 512
|
||||
|
||||
/************************************************************************
|
||||
* Minor device decoding conventions.
|
||||
************************************************************************
|
||||
*
|
||||
* For Linux, the net and mon devices are handled via "proc", so we
|
||||
* only have to mux the "tty" devices. Since every PortServer will
|
||||
* have an individual major number, the PortServer number does not
|
||||
* need to be encoded, and in fact, does not need to exist.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Port device decoding conventions:
|
||||
*
|
||||
* Device 00 - 3f 64 dial-in modem devices. (tty)
|
||||
* Device 40 - 7f 64 dial-out tty devices. (cu)
|
||||
* Device 80 - bf 64 dial-out printer devices.
|
||||
*
|
||||
* IS_PRINT(dev) This is a printer device.
|
||||
*
|
||||
* OPEN_CATEGORY(dev) Specifies the device category. No two
|
||||
* devices of different categories may be open
|
||||
* at the same time.
|
||||
*
|
||||
* The following require the category returned by OPEN_CATEGORY().
|
||||
*
|
||||
* OPEN_WAIT_AVAIL(cat) Waits on open until the device becomes
|
||||
* available. Fails if NDELAY specified.
|
||||
*
|
||||
* OPEN_WAIT_CARRIER(cat) Waits on open if carrier is not present.
|
||||
* Succeeds if NDELAY is given.
|
||||
*
|
||||
* OPEN_FORCES_CARRIER(cat) Carrier is forced high on open.
|
||||
*
|
||||
*/
|
||||
|
||||
#define PORT_NUM(dev) ((dev) & 0x3f)
|
||||
|
||||
#define OPEN_CATEGORY(dev) ((((dev) & 0x80) & 0x40))
|
||||
#define IS_PRINT(dev) (((dev) & 0xff) >= 0x80)
|
||||
|
||||
#define OPEN_WAIT_AVAIL(cat) (((cat) & 0x40) == 0x000)
|
||||
#define OPEN_WAIT_CARRIER(cat) (((cat) & 0x40) == 0x000)
|
||||
#define OPEN_FORCES_CARRIER(cat) (((cat) & 0x40) != 0x000)
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Modem signal defines for 16450/16550 compatible FEP.
|
||||
* set in ch_mout, ch_mflow, ch_mlast etc
|
||||
************************************************************************/
|
||||
|
||||
/* TODO : Re-verify that these modem signal definitions are correct */
|
||||
|
||||
#define DM_DTR 0x01
|
||||
#define DM_RTS 0x02
|
||||
#define DM_RTS_TOGGLE 0x04
|
||||
|
||||
#define DM_OUT1 0x04
|
||||
#define DM_OUT2 0x08
|
||||
|
||||
#define DM_CTS 0x10
|
||||
#define DM_DSR 0x20
|
||||
#define DM_RI 0x40
|
||||
#define DM_CD 0x80 /* This is the DCD flag */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Realport Event Flags.
|
||||
************************************************************************/
|
||||
|
||||
#define EV_OPU 0x0001 /* Ouput paused by client */
|
||||
#define EV_OPS 0x0002 /* Output paused by XOFF */
|
||||
#define EV_OPX 0x0004 /* Output paused by XXOFF */
|
||||
#define EV_OPH 0x0008 /* Output paused by MFLOW */
|
||||
#define EV_IPU 0x0010 /* Input paused by client */
|
||||
#define EV_IPS 0x0020 /* Input paused by hi/low water */
|
||||
#define EV_TXB 0x0040 /* Transmit break pending */
|
||||
#define EV_TXI 0x0080 /* Transmit immediate pending */
|
||||
#define EV_TXF 0x0100 /* Transmit flow control pending */
|
||||
#define EV_RXB 0x0200 /* Break received */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Realport CFLAGS.
|
||||
************************************************************************/
|
||||
|
||||
#define CF_CS5 0x0000 /* 5 bit characters */
|
||||
#define CF_CS6 0x0010 /* 6 bit characters */
|
||||
#define CF_CS7 0x0020 /* 7 bit characters */
|
||||
#define CF_CS8 0x0030 /* 8 bit characters */
|
||||
#define CF_CSIZE 0x0030 /* Character size */
|
||||
#define CF_CSTOPB 0x0040 /* Two stop bits */
|
||||
#define CF_CREAD 0x0080 /* Enable receiver */
|
||||
#define CF_PARENB 0x0100 /* Enable parity */
|
||||
#define CF_PARODD 0x0200 /* Odd parity */
|
||||
#define CF_HUPCL 0x0400 /* Drop DTR on close */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Realport XFLAGS.
|
||||
************************************************************************/
|
||||
|
||||
#define XF_XPAR 0x0001 /* Enable Mark/Space Parity */
|
||||
#define XF_XMODEM 0x0002 /* Enable in-band modem signalling */
|
||||
#define XF_XCASE 0x0004 /* Convert special characters */
|
||||
#define XF_XEDATA 0x0008 /* Error data in stream */
|
||||
#define XF_XTOSS 0x0010 /* Toss IXANY characters */
|
||||
#define XF_XIXON 0x0020 /* xxon/xxoff enable */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Realport IFLAGS.
|
||||
************************************************************************/
|
||||
|
||||
#define IF_IGNBRK 0x0001 /* Ignore input break */
|
||||
#define IF_BRKINT 0x0002 /* Break interrupt */
|
||||
#define IF_IGNPAR 0x0004 /* Ignore error characters */
|
||||
#define IF_PARMRK 0x0008 /* Error chars marked with 0xff */
|
||||
#define IF_INPCK 0x0010 /* Input parity checking enabled */
|
||||
#define IF_ISTRIP 0x0020 /* Input chars masked with 0x7F */
|
||||
#define IF_IXON 0x0400 /* Output software flow control */
|
||||
#define IF_IXANY 0x0800 /* Restart output on any char */
|
||||
#define IF_IXOFF 0x1000 /* Input software flow control */
|
||||
#define IF_DOSMODE 0x8000 /* 16450-compatible errors */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Realport OFLAGS.
|
||||
************************************************************************/
|
||||
|
||||
#define OF_OLCUC 0x0002 /* Map lower to upper case */
|
||||
#define OF_ONLCR 0x0004 /* Map NL to CR-NL */
|
||||
#define OF_OCRNL 0x0008 /* Map CR to NL */
|
||||
#define OF_ONOCR 0x0010 /* No CR output at column 0 */
|
||||
#define OF_ONLRET 0x0020 /* Assume NL does NL/CR */
|
||||
#define OF_TAB3 0x1800 /* Tabs expand to 8 spaces */
|
||||
#define OF_TABDLY 0x1800 /* Tab delay */
|
||||
|
||||
/************************************************************************
|
||||
* Unit flag definitions for un_flag.
|
||||
************************************************************************/
|
||||
|
||||
/* These are the DIGI unit flags */
|
||||
#define UN_EXCL 0x00010000 /* Exclusive open */
|
||||
#define UN_STICKY 0x00020000 /* TTY Settings are now sticky */
|
||||
#define UN_BUSY 0x00040000 /* Some work this channel */
|
||||
#define UN_PWAIT 0x00080000 /* Printer waiting for terminal */
|
||||
#define UN_TIME 0x00100000 /* Waiting on time */
|
||||
#define UN_EMPTY 0x00200000 /* Waiting output queue empty */
|
||||
#define UN_LOW 0x00400000 /* Waiting output low water */
|
||||
#define UN_DIGI_MASK 0x00FF0000 /* Waiting output low water */
|
||||
|
||||
/*
|
||||
* Definitions for async_struct (and serial_struct) flags field
|
||||
*
|
||||
* these are the ASYNC flags copied from serial.h
|
||||
*
|
||||
*/
|
||||
#define UN_HUP_NOTIFY 0x0001 /* Notify getty on hangups and
|
||||
* closes on the callout port
|
||||
*/
|
||||
#define UN_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
|
||||
#define UN_SAK 0x0004 /* Secure Attention Key (Orange book) */
|
||||
#define UN_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
|
||||
|
||||
#define UN_SPD_MASK 0x0030
|
||||
#define UN_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
|
||||
#define UN_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
|
||||
#define UN_SPD_CUST 0x0030 /* Use user-specified divisor */
|
||||
|
||||
#define UN_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
|
||||
#define UN_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
|
||||
|
||||
#define UN_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
|
||||
#define UN_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
|
||||
#define UN_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
|
||||
|
||||
#define UN_FLAGS 0x0FFF /* Possible legal async flags */
|
||||
#define UN_USR_MASK 0x0430 /* Legal flags that non-privileged
|
||||
* users can set or reset
|
||||
*/
|
||||
|
||||
#define UN_INITIALIZED 0x80000000 /* Serial port was initialized */
|
||||
#define UN_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
|
||||
#define UN_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
|
||||
#define UN_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
|
||||
#define UN_CLOSING 0x08000000 /* Serial port is closing */
|
||||
#define UN_CTS_FLOW 0x04000000 /* Do CTS flow control */
|
||||
#define UN_CHECK_CD 0x02000000 /* i.e., CLOCAL */
|
||||
#define UN_SHARE_IRQ 0x01000000 /* for multifunction cards */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Structure for terminal or printer unit. struct un_struct
|
||||
*
|
||||
* Note that in some places the code assumes the "tty_t" is placed
|
||||
* first in the structure.
|
||||
************************************************************************/
|
||||
|
||||
struct un_struct {
|
||||
struct tty_struct *un_tty; /* System TTY struct */
|
||||
struct ch_struct *un_ch; /* Associated channel */
|
||||
|
||||
ushort un_open_count; /* Successful open count */
|
||||
int un_flag; /* Unit flags */
|
||||
ushort un_tbusy; /* Busy transmit count */
|
||||
|
||||
wait_queue_head_t un_open_wait;
|
||||
wait_queue_head_t un_close_wait;
|
||||
ushort un_type;
|
||||
struct device *un_sysfs;
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Channel State Numbers for ch_state.
|
||||
************************************************************************/
|
||||
|
||||
/*
|
||||
* The ordering is important.
|
||||
*
|
||||
* state <= CS_WAIT_CANCEL implies the channel is definitely closed.
|
||||
*
|
||||
* state >= CS_WAIT_FAIL implies the channel is definitely open.
|
||||
*
|
||||
* state >= CS_READY implies data is allowed on the channel.
|
||||
*/
|
||||
|
||||
enum dgrp_ch_state_t {
|
||||
CS_IDLE = 0, /* Channel is idle */
|
||||
CS_WAIT_OPEN = 1, /* Waiting for Immediate Open Resp */
|
||||
CS_WAIT_CANCEL = 2, /* Waiting for Per/Incom Cancel Resp */
|
||||
CS_WAIT_FAIL = 3, /* Waiting for Immed Open Failure */
|
||||
CS_SEND_QUERY = 4, /* Ready to send Port Query */
|
||||
CS_WAIT_QUERY = 5, /* Waiting for Port Query Response */
|
||||
CS_READY = 6, /* Ready to accept commands and data */
|
||||
CS_SEND_CLOSE = 7, /* Ready to send Close Request */
|
||||
CS_WAIT_CLOSE = 8 /* Waiting for Close Response */
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* Device flag definitions for ch_flag.
|
||||
************************************************************************/
|
||||
|
||||
/*
|
||||
* Note that the state of the two carrier based flags is key. When
|
||||
* we check for carrier state transitions, we look at the current
|
||||
* physical state of the DCD line and compare it with PHYS_CD (which
|
||||
* was the state the last time we checked), and we also determine
|
||||
* a new virtual state (composite of the physical state, FORCEDCD,
|
||||
* CLOCAL, etc.) and compare it with VIRT_CD.
|
||||
*
|
||||
* VIRTUAL transitions high will have the side effect of waking blocked
|
||||
* opens.
|
||||
*
|
||||
* PHYSICAL transitions low will cause hangups to occur _IF_ the virtual
|
||||
* state is also low. We DON'T want to hangup on a PURE virtual drop.
|
||||
*/
|
||||
|
||||
#define CH_HANGUP 0x00002 /* Server port ready to close */
|
||||
|
||||
#define CH_VIRT_CD 0x00004 /* Carrier was virtually present */
|
||||
#define CH_PHYS_CD 0x00008 /* Carrier was physically present */
|
||||
|
||||
#define CH_CLOCAL 0x00010 /* CLOCAL set in cflags */
|
||||
#define CH_BAUD0 0x00020 /* Baud rate zero hangup */
|
||||
|
||||
#define CH_FAST_READ 0x00040 /* Fast reads are enabled */
|
||||
#define CH_FAST_WRITE 0x00080 /* Fast writes are enabled */
|
||||
|
||||
#define CH_PRON 0x00100 /* Printer on string active */
|
||||
#define CH_RX_FLUSH 0x00200 /* Flushing receive data */
|
||||
#define CH_LOW 0x00400 /* Thread waiting for LOW water */
|
||||
#define CH_EMPTY 0x00800 /* Thread waiting for EMPTY */
|
||||
#define CH_DRAIN 0x01000 /* Close is waiting to drain */
|
||||
#define CH_INPUT 0x02000 /* Thread waiting for INPUT */
|
||||
#define CH_RXSTOP 0x04000 /* Stop output to ldisc */
|
||||
#define CH_PARAM 0x08000 /* A parameter was updated */
|
||||
#define CH_WAITING_SYNC 0x10000 /* A pending sync was assigned
|
||||
* to this port.
|
||||
*/
|
||||
#define CH_PORT_GONE 0x20000 /* Port has disappeared */
|
||||
#define CH_TX_BREAK 0x40000 /* TX Break to be sent,
|
||||
* but has not yet.
|
||||
*/
|
||||
|
||||
/************************************************************************
|
||||
* Types of Open Requests for ch_otype.
|
||||
************************************************************************/
|
||||
|
||||
#define OTYPE_IMMEDIATE 0 /* Immediate Open */
|
||||
#define OTYPE_PERSISTENT 1 /* Persistent Open */
|
||||
#define OTYPE_INCOMING 2 /* Incoming Open */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Request/Response flags.
|
||||
************************************************************************/
|
||||
|
||||
#define RR_SEQUENCE 0x0001 /* Get server RLAST, TIN */
|
||||
#define RR_STATUS 0x0002 /* Get server MINT, EINT */
|
||||
#define RR_BUFFER 0x0004 /* Get server RSIZE, TSIZE */
|
||||
#define RR_CAPABILITY 0x0008 /* Get server port capabilities */
|
||||
|
||||
#define RR_TX_FLUSH 0x0040 /* Flush output buffers */
|
||||
#define RR_RX_FLUSH 0x0080 /* Flush input buffers */
|
||||
|
||||
#define RR_TX_STOP 0x0100 /* Pause output */
|
||||
#define RR_RX_STOP 0x0200 /* Pause input */
|
||||
#define RR_TX_START 0x0400 /* Start output */
|
||||
#define RR_RX_START 0x0800 /* Start input */
|
||||
|
||||
#define RR_TX_BREAK 0x1000 /* Send BREAK */
|
||||
#define RR_TX_ICHAR 0x2000 /* Send character immediate */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Channel information structure. struct ch_struct
|
||||
************************************************************************/
|
||||
|
||||
struct ch_struct {
|
||||
struct digi_struct ch_digi; /* Digi variables */
|
||||
int ch_edelay; /* Digi edelay */
|
||||
|
||||
struct tty_port port;
|
||||
struct un_struct ch_tun; /* Terminal unit info */
|
||||
struct un_struct ch_pun; /* Printer unit info */
|
||||
|
||||
struct nd_struct *ch_nd; /* Node pointer */
|
||||
u8 *ch_tbuf; /* Local Transmit Buffer */
|
||||
u8 *ch_rbuf; /* Local Receive Buffer */
|
||||
ulong ch_cpstime; /* Printer CPS time */
|
||||
ulong ch_waketime; /* Printer wake time */
|
||||
|
||||
ulong ch_flag; /* CH_* flags */
|
||||
|
||||
enum dgrp_ch_state_t ch_state; /* CS_* Protocol state */
|
||||
ushort ch_send; /* Bit vector of RR_* requests */
|
||||
ushort ch_expect; /* Bit vector of RR_* responses */
|
||||
ushort ch_wait_carrier; /* Thread count waiting for carrier */
|
||||
ushort ch_wait_count[3]; /* Thread count waiting by otype */
|
||||
|
||||
ushort ch_portnum; /* Port number */
|
||||
ushort ch_open_count; /* Successful open count */
|
||||
ushort ch_category; /* Device category */
|
||||
ushort ch_open_error; /* Last open error number */
|
||||
ushort ch_break_time; /* Pending break request time */
|
||||
ushort ch_cpsrem; /* Printer CPS remainder */
|
||||
ushort ch_ocook; /* Realport fastcook oflags */
|
||||
ushort ch_inwait; /* Thread count in CLIST input */
|
||||
|
||||
ushort ch_tin; /* Local transmit buffer in ptr */
|
||||
ushort ch_tout; /* Local transmit buffer out ptr */
|
||||
ushort ch_s_tin; /* Realport TIN */
|
||||
ushort ch_s_tpos; /* Realport TPOS */
|
||||
ushort ch_s_tsize; /* Realport TSIZE */
|
||||
ushort ch_s_treq; /* Realport TREQ */
|
||||
ushort ch_s_elast; /* Realport ELAST */
|
||||
|
||||
ushort ch_rin; /* Local receive buffer in ptr */
|
||||
ushort ch_rout; /* Local receive buffer out ptr */
|
||||
ushort ch_s_rin; /* Realport RIN */
|
||||
/* David Fries 7-13-2001, ch_s_rin should be renamed ch_s_rout because
|
||||
* the variable we want to represent is the PortServer's ROUT, which is
|
||||
* the sequence number for the next byte the PortServer will send us.
|
||||
* RIN is the sequence number for the next byte the PortServer will
|
||||
* receive from the uart. The port server will send data as long as
|
||||
* ROUT is less than RWIN. What would happen is the port is opened, it
|
||||
* receives data, it gives the value of RIN, we set the RWIN to
|
||||
* RIN+RBUF_MAX-1, it sends us RWIN-ROUT bytes which overflows. ROUT
|
||||
* is set to zero when the port is opened, so we start at zero and
|
||||
* count up as data is received.
|
||||
*/
|
||||
ushort ch_s_rwin; /* Realport RWIN */
|
||||
ushort ch_s_rsize; /* Realport RSIZE */
|
||||
|
||||
ushort ch_tmax; /* Local TMAX */
|
||||
ushort ch_ttime; /* Local TTIME */
|
||||
ushort ch_rmax; /* Local RMAX */
|
||||
ushort ch_rtime; /* Local RTIME */
|
||||
ushort ch_rlow; /* Local RLOW */
|
||||
ushort ch_rhigh; /* Local RHIGH */
|
||||
|
||||
ushort ch_s_tmax; /* Realport TMAX */
|
||||
ushort ch_s_ttime; /* Realport TTIME */
|
||||
ushort ch_s_rmax; /* Realport RMAX */
|
||||
ushort ch_s_rtime; /* Realport RTIME */
|
||||
ushort ch_s_rlow; /* Realport RLOW */
|
||||
ushort ch_s_rhigh; /* Realport RHIGH */
|
||||
|
||||
ushort ch_brate; /* Local baud rate */
|
||||
ushort ch_cflag; /* Local tty cflags */
|
||||
ushort ch_iflag; /* Local tty iflags */
|
||||
ushort ch_oflag; /* Local tty oflags */
|
||||
ushort ch_xflag; /* Local tty xflags */
|
||||
|
||||
ushort ch_s_brate; /* Realport BRATE */
|
||||
ushort ch_s_cflag; /* Realport CFLAG */
|
||||
ushort ch_s_iflag; /* Realport IFLAG */
|
||||
ushort ch_s_oflag; /* Realport OFLAG */
|
||||
ushort ch_s_xflag; /* Realport XFLAG */
|
||||
|
||||
u8 ch_otype; /* Open request type */
|
||||
u8 ch_pscan_savechar; /* Last character read by parity scan */
|
||||
u8 ch_pscan_state; /* PScan State based on last 2 chars */
|
||||
u8 ch_otype_waiting; /* Type of open pending in server */
|
||||
u8 ch_flush_seq; /* Receive flush end sequence */
|
||||
u8 ch_s_mlast; /* Realport MLAST */
|
||||
|
||||
u8 ch_mout; /* Local MOUT */
|
||||
u8 ch_mflow; /* Local MFLOW */
|
||||
u8 ch_mctrl; /* Local MCTRL */
|
||||
u8 ch_xon; /* Local XON */
|
||||
u8 ch_xoff; /* Local XOFF */
|
||||
u8 ch_lnext; /* Local LNEXT */
|
||||
u8 ch_xxon; /* Local XXON */
|
||||
u8 ch_xxoff; /* Local XXOFF */
|
||||
|
||||
u8 ch_s_mout; /* Realport MOUT */
|
||||
u8 ch_s_mflow; /* Realport MFLOW */
|
||||
u8 ch_s_mctrl; /* Realport MCTRL */
|
||||
u8 ch_s_xon; /* Realport XON */
|
||||
u8 ch_s_xoff; /* Realport XOFF */
|
||||
u8 ch_s_lnext; /* Realport LNEXT */
|
||||
u8 ch_s_xxon; /* Realport XXON */
|
||||
u8 ch_s_xxoff; /* Realport XXOFF */
|
||||
|
||||
wait_queue_head_t ch_flag_wait; /* Wait queue for ch_flag changes */
|
||||
wait_queue_head_t ch_sleep; /* Wait queue for my_sleep() */
|
||||
|
||||
int ch_custom_speed; /* Realport custom speed */
|
||||
int ch_txcount; /* Running TX count */
|
||||
int ch_rxcount; /* Running RX count */
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Node State definitions.
|
||||
************************************************************************/
|
||||
|
||||
enum dgrp_nd_state_t {
|
||||
NS_CLOSED = 0, /* Network device is closed */
|
||||
NS_IDLE = 1, /* Network connection inactive */
|
||||
NS_SEND_QUERY = 2, /* Send server query */
|
||||
NS_WAIT_QUERY = 3, /* Wait for query response */
|
||||
NS_READY = 4, /* Network ready */
|
||||
NS_SEND_ERROR = 5 /* Must send error hangup */
|
||||
};
|
||||
|
||||
#define ND_STATE_STR(x) \
|
||||
((x) == NS_CLOSED ? "CLOSED" : \
|
||||
((x) == NS_IDLE ? "IDLE" : \
|
||||
((x) == NS_SEND_QUERY ? "SEND_QUERY" : \
|
||||
((x) == NS_WAIT_QUERY ? "WAIT_QUERY" : \
|
||||
((x) == NS_READY ? "READY" : \
|
||||
((x) == NS_SEND_ERROR ? "SEND_ERROR" : "UNKNOWN"))))))
|
||||
|
||||
/************************************************************************
|
||||
* Node Flag definitions.
|
||||
************************************************************************/
|
||||
|
||||
#define ND_SELECT 0x0001 /* Multiple net read selects */
|
||||
#define ND_DEB_WAIT 0x0002 /* Debug Device waiting */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Monitoring flag definitions.
|
||||
************************************************************************/
|
||||
|
||||
#define MON_WAIT_DATA 0x0001 /* Waiting for buffer data */
|
||||
#define MON_WAIT_SPACE 0x0002 /* Waiting for buffer space */
|
||||
|
||||
/************************************************************************
|
||||
* DPA flag definitions.
|
||||
************************************************************************/
|
||||
|
||||
#define DPA_WAIT_DATA 0x0001 /* Waiting for buffer data */
|
||||
#define DPA_WAIT_SPACE 0x0002 /* Waiting for buffer space */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Definitions taken from Realport Dump.
|
||||
************************************************************************/
|
||||
|
||||
#define RPDUMP_MAGIC "Digi-RealPort-1.0"
|
||||
|
||||
#define RPDUMP_MESSAGE 0xE2 /* Descriptive message */
|
||||
#define RPDUMP_RESET 0xE7 /* Connection reset */
|
||||
#define RPDUMP_CLIENT 0xE8 /* Client data */
|
||||
#define RPDUMP_SERVER 0xE9 /* Server data */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Node request/response definitions.
|
||||
************************************************************************/
|
||||
|
||||
#define NR_ECHO 0x0001 /* Server echo packet */
|
||||
#define NR_IDENT 0x0002 /* Server Product ID */
|
||||
#define NR_CAPABILITY 0x0004 /* Server Capabilties */
|
||||
#define NR_VPD 0x0008 /* Server VPD, if any */
|
||||
#define NR_PASSWORD 0x0010 /* Server Password */
|
||||
|
||||
/************************************************************************
|
||||
* Registration status of the node's Linux struct tty_driver structures.
|
||||
************************************************************************/
|
||||
#define SERIAL_TTDRV_REG 0x0001 /* nd_serial_ttdriver registered */
|
||||
#define CALLOUT_TTDRV_REG 0x0002 /* nd_callout_ttdriver registered */
|
||||
#define XPRINT_TTDRV_REG 0x0004 /* nd_xprint_ttdriver registered */
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Node structure. There exists one of these for each associated
|
||||
* realport server.
|
||||
************************************************************************/
|
||||
|
||||
struct nd_struct {
|
||||
struct list_head list;
|
||||
long nd_major; /* Node's major number */
|
||||
long nd_ID; /* Node's ID code */
|
||||
|
||||
char nd_serial_name[50]; /* "tty_dgrp_<id>_" + null */
|
||||
char nd_callout_name[50]; /* "cu_dgrp_<id>_" + null */
|
||||
char nd_xprint_name[50]; /* "pr_dgrp_<id>_" + null */
|
||||
|
||||
char password[16]; /* Password for server, if needed */
|
||||
int nd_tty_ref_cnt; /* Linux tty reference count */
|
||||
|
||||
struct proc_dir_entry *nd_net_de; /* Dir entry for /proc/dgrp/net */
|
||||
struct proc_dir_entry *nd_mon_de; /* Dir entry for /proc/dgrp/mon */
|
||||
struct proc_dir_entry *nd_ports_de; /* Dir entry for /proc/dgrp/ports*/
|
||||
struct proc_dir_entry *nd_dpa_de; /* Dir entry for /proc/dgrp/dpa */
|
||||
|
||||
spinlock_t nd_lock; /* General node lock */
|
||||
|
||||
struct semaphore nd_net_semaphore; /* Net read/write lock */
|
||||
struct semaphore nd_mon_semaphore; /* Monitor buffer lock */
|
||||
spinlock_t nd_dpa_lock; /* DPA buffer lock */
|
||||
|
||||
enum dgrp_nd_state_t nd_state; /* NS_* network state */
|
||||
int nd_chan_count; /* # active channels */
|
||||
int nd_flag; /* Node flags */
|
||||
int nd_send; /* Responses to send */
|
||||
int nd_expect; /* Responses we expect */
|
||||
|
||||
u8 *nd_iobuf; /* Network R/W Buffer */
|
||||
wait_queue_head_t nd_tx_waitq; /* Network select wait queue */
|
||||
|
||||
u8 *nd_inputbuf; /* Input Buffer */
|
||||
u8 *nd_inputflagbuf; /* Input Flags Buffer */
|
||||
|
||||
int nd_tx_deposit; /* Accumulated transmit deposits */
|
||||
int nd_tx_charge; /* Accumulated transmit charges */
|
||||
int nd_tx_credit; /* Current TX credit */
|
||||
int nd_tx_ready; /* Ready to transmit */
|
||||
int nd_tx_work; /* TX work waiting */
|
||||
ulong nd_tx_time; /* Last transmit time */
|
||||
ulong nd_poll_time; /* Next scheduled poll time */
|
||||
|
||||
int nd_delay; /* Current TX delay */
|
||||
int nd_rate; /* Current TX rate */
|
||||
struct link_struct nd_link; /* Link speed params. */
|
||||
|
||||
int nd_seq_in; /* TX seq in ptr */
|
||||
int nd_seq_out; /* TX seq out ptr */
|
||||
int nd_unack; /* Unacknowledged byte count */
|
||||
int nd_remain; /* Remaining receive bytes */
|
||||
int nd_tx_module; /* Current TX module # */
|
||||
int nd_rx_module; /* Current RX module # */
|
||||
char *nd_error; /* Protocol error message */
|
||||
|
||||
int nd_write_count; /* drp_write() call count */
|
||||
int nd_read_count; /* drp_read() count */
|
||||
int nd_send_count; /* TCP message sent */
|
||||
int nd_tx_byte; /* Transmit byte count */
|
||||
int nd_rx_byte; /* Receive byte count */
|
||||
|
||||
ulong nd_mon_lbolt; /* Monitor start time */
|
||||
int nd_mon_flag; /* Monitor flags */
|
||||
int nd_mon_in; /* Monitor in pointer */
|
||||
int nd_mon_out; /* Monitor out pointer */
|
||||
wait_queue_head_t nd_mon_wqueue; /* Monitor wait queue (on flags) */
|
||||
u8 *nd_mon_buf; /* Monitor buffer */
|
||||
|
||||
ulong nd_dpa_lbolt; /* DPA start time */
|
||||
int nd_dpa_flag; /* DPA flags */
|
||||
int nd_dpa_in; /* DPA in pointer */
|
||||
int nd_dpa_out; /* DPA out pointer */
|
||||
wait_queue_head_t nd_dpa_wqueue; /* DPA wait queue (on flags) */
|
||||
u8 *nd_dpa_buf; /* DPA buffer */
|
||||
|
||||
uint nd_dpa_debug;
|
||||
uint nd_dpa_port;
|
||||
|
||||
wait_queue_head_t nd_seq_wque[SEQ_MAX]; /* TX thread wait queues */
|
||||
u8 nd_seq_wait[SEQ_MAX]; /* Transmit thread wait count */
|
||||
|
||||
ushort nd_seq_size[SEQ_MAX]; /* Transmit seq packet size */
|
||||
ulong nd_seq_time[SEQ_MAX]; /* Transmit seq packet time */
|
||||
|
||||
ushort nd_hw_ver; /* HW version returned from PS */
|
||||
ushort nd_sw_ver; /* SW version returned from PS */
|
||||
uint nd_hw_id; /* HW ID returned from PS */
|
||||
u8 nd_ps_desc[MAX_DESC_LEN+1]; /* Description from PS */
|
||||
uint nd_vpd_len; /* VPD len, if any */
|
||||
u8 nd_vpd[VPDSIZE]; /* VPD, if any */
|
||||
|
||||
ulong nd_ttdriver_flags; /* Registration status */
|
||||
struct tty_driver *nd_serial_ttdriver; /* Linux TTYDRIVER structure */
|
||||
struct tty_driver *nd_callout_ttdriver; /* Linux TTYDRIVER structure */
|
||||
struct tty_driver *nd_xprint_ttdriver; /* Linux TTYDRIVER structure */
|
||||
|
||||
u8 *nd_writebuf; /* Used to cache data read
|
||||
* from user
|
||||
*/
|
||||
struct ch_struct nd_chan[CHAN_MAX]; /* Channel array */
|
||||
struct device *nd_class_dev; /* Hang our sysfs stuff off of here */
|
||||
};
|
||||
|
||||
#endif /* __DRP_H */
|
Loading…
Reference in New Issue