提交 19b1e769 编写于 作者: K Kristina Martšenko 提交者: Greg Kroah-Hartman

staging: dgrp: remove driver

Remove the driver as it hasn't been cleaned up and it doesn't look like
anyone is going to work on it anymore. This can be reverted if someone
wants to work to fix the remaining issues the driver has.
Signed-off-by: NKristina Martšenko <kristina.martsenko@gmail.com>
Cc: Bill Pemberton <wfp5p@worldbroken.com>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
上级 929b2d48
......@@ -114,8 +114,6 @@ source "drivers/staging/ced1401/Kconfig"
source "drivers/staging/imx-drm/Kconfig"
source "drivers/staging/dgrp/Kconfig"
source "drivers/staging/fwserial/Kconfig"
source "drivers/staging/goldfish/Kconfig"
......
......@@ -50,7 +50,6 @@ obj-$(CONFIG_LTE_GDM724X) += gdm724x/
obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/
obj-$(CONFIG_CED1401) += ced1401/
obj-$(CONFIG_DRM_IMX) += imx-drm/
obj-$(CONFIG_DGRP) += dgrp/
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_LUSTRE_FS) += lustre/
......
config DGRP
tristate "Digi Realport driver"
default n
depends on SYSFS && TTY
---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.
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
The user space code to work with this driver is located at
https://github.com/wfp5p/dgrp-utils
- Use configfs for config stuff. This will require changes to the
user space code.
- dgrp_send() and dgrp_receive() could use some refactoring
- Don't automatically create CHAN_MAX (64) channel array entries for
every device as many devices are going to have much less than 64
channels.
- The locking needs to be checked. It seems haphazardly done in most
places.
- Check Kconfig dependencies
/*
*
* 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;
}
/*
*
* 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_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 const struct file_operations dgrp_mon_ops;
/* 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 const struct file_operations dgrp_ports_ops;
/* from dgrp_net_ops.c */
extern const struct file_operations dgrp_net_ops;
/* from dgrp_dpa_ops.c */
extern const struct file_operations dgrp_dpa_ops;
extern void dgrp_dpa_data(struct nd_struct *, int, u8 *, int);
/* from dgrp_sysfs.c */
extern int 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 */
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);
/*
* 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 */
/*
*
* 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 <linux/slab.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 *);
const struct file_operations dgrp_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,
};
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 */
/*
* 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;
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.
*/
nd = PDE_DATA(inode);
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 > 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);
strlcpy(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:
memset(&vpd, 0, sizeof(vpd));
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);
}
/*
*
* 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>
/*
* 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_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(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");
/*
* init_module()
*
* Module load. This is where it all starts.
*/
static int __init dgrp_init_module(void)
{
int ret;
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;
ret = dgrp_create_class_sysfs_files();
if (ret)
return ret;
dgrp_register_proc();
return 0;
}
/*
* Module unload. This is where it all ends.
*/
static void __exit 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);
}
}
module_init(dgrp_init_module);
module_exit(dgrp_cleanup_module);
/*****************************************************************************
*
* 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/slab.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.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);
const struct file_operations dgrp_mon_ops = {
.owner = THIS_MODULE,
.read = dgrp_mon_read,
.unlocked_ioctl = dgrp_mon_ioctl,
.open = dgrp_mon_open,
.release = dgrp_mon_release,
};
/**
* 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 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.
*/
nd = PDE_DATA(inode);
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;
}
此差异已折叠。
/*
*
* 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 *);
const struct file_operations dgrp_ports_ops = {
.owner = THIS_MODULE,
.open = dgrp_ports_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
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_DATA(inode);
}
return rtn;
}
/*
*
* 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/slab.h>
#include <linux/ctype.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include "dgrp_common.h"
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 struct proc_dir_entry *add_proc_file(struct nd_struct *node,
struct proc_dir_entry *root,
const struct file_operations *fops);
/* File operation declarations */
static int parse_write_config(char *);
static ssize_t dgrp_config_proc_write(struct file *file,
const char __user *buffer,
size_t count, loff_t *pos);
static int dgrp_nodeinfo_proc_open(struct inode *inode, struct file *file);
static int dgrp_info_proc_open(struct inode *inode, struct file *file);
static int dgrp_config_proc_open(struct inode *inode, struct file *file);
static const struct file_operations config_proc_file_ops = {
.owner = THIS_MODULE,
.open = dgrp_config_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.write = dgrp_config_proc_write,
};
static const struct file_operations info_proc_file_ops = {
.owner = THIS_MODULE,
.open = dgrp_info_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations nodeinfo_proc_file_ops = {
.owner = THIS_MODULE,
.open = dgrp_nodeinfo_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
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 void remove_files(struct nd_struct *nd)
{
char buf[3];
ID_TO_CHAR(nd->nd_ID, buf);
dgrp_remove_node_class_sysfs_files(nd);
if (nd->nd_net_de)
remove_proc_entry(buf, net_entry_pointer);
if (nd->nd_mon_de)
remove_proc_entry(buf, mon_entry_pointer);
if (nd->nd_dpa_de)
remove_proc_entry(buf, dpa_entry_pointer);
if (nd->nd_ports_de)
remove_proc_entry(buf, ports_entry_pointer);
}
void dgrp_unregister_proc(void)
{
net_entry_pointer = NULL;
mon_entry_pointer = NULL;
dpa_entry_pointer = NULL;
ports_entry_pointer = NULL;
if (dgrp_proc_dir_entry) {
struct nd_struct *nd;
list_for_each_entry(nd, &nd_struct_list, list)
remove_files(nd);
remove_proc_entry("dgrp/config", NULL);
remove_proc_entry("dgrp/info", NULL);
remove_proc_entry("dgrp/nodeinfo", NULL);
remove_proc_entry("dgrp/net", NULL);
remove_proc_entry("dgrp/mon", NULL);
remove_proc_entry("dgrp/dpa", NULL);
remove_proc_entry("dgrp/ports", NULL);
remove_proc_entry("dgrp", NULL);
dgrp_proc_dir_entry = NULL;
}
}
void dgrp_register_proc(void)
{
/*
* Register /proc/dgrp
*/
dgrp_proc_dir_entry = proc_mkdir("dgrp", NULL);
if (!dgrp_proc_dir_entry)
return;
proc_create("dgrp/config", 0644, NULL, &config_proc_file_ops);
proc_create("dgrp/info", 0644, NULL, &info_proc_file_ops);
proc_create("dgrp/nodeinfo", 0644, NULL, &nodeinfo_proc_file_ops);
net_entry_pointer = proc_mkdir_mode("dgrp/net", 0500, NULL);
mon_entry_pointer = proc_mkdir_mode("dgrp/mon", 0500, NULL);
dpa_entry_pointer = proc_mkdir_mode("dgrp/dpa", 0500, NULL);
ports_entry_pointer = proc_mkdir_mode("dgrp/ports", 0500, NULL);
}
static void *dgrp_config_proc_start(struct seq_file *m, loff_t *pos)
{
return seq_list_start_head(&nd_struct_list, *pos);
}
static void *dgrp_config_proc_next(struct seq_file *p, void *v, loff_t *pos)
{
return seq_list_next(v, &nd_struct_list, pos);
}
static void dgrp_config_proc_stop(struct seq_file *m, void *v)
{
}
static int dgrp_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 = dgrp_config_proc_start,
.next = dgrp_config_proc_next,
.stop = dgrp_config_proc_stop,
.show = dgrp_config_proc_show,
};
static int dgrp_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 dgrp_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 dgrp_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, "pollrate: 0x%08x\t(%d)\n",
dgrp_poll_tick, dgrp_poll_tick);
return 0;
}
static int dgrp_info_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, dgrp_info_proc_show, NULL);
}
static void *dgrp_nodeinfo_start(struct seq_file *m, loff_t *pos)
{
return seq_list_start_head(&nd_struct_list, *pos);
}
static void *dgrp_nodeinfo_next(struct seq_file *p, void *v, loff_t *pos)
{
return seq_list_next(v, &nd_struct_list, pos);
}
static void dgrp_nodeinfo_stop(struct seq_file *m, void *v)
{
}
static int dgrp_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 = dgrp_nodeinfo_start,
.next = dgrp_nodeinfo_next,
.stop = dgrp_nodeinfo_stop,
.show = dgrp_nodeinfo_show,
};
static int dgrp_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);
sema_init(&nd->nd_mon_semaphore, 1);
sema_init(&nd->nd_net_semaphore, 1);
spin_lock_init(&nd->nd_dpa_lock);
nd->nd_state = NS_CLOSED;
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;
dgrp_create_node_class_sysfs_files(nd);
nd->nd_net_de = add_proc_file(nd, net_entry_pointer, &dgrp_net_ops);
nd->nd_mon_de = add_proc_file(nd, mon_entry_pointer, &dgrp_mon_ops);
nd->nd_dpa_de = add_proc_file(nd, dpa_entry_pointer, &dgrp_dpa_ops);
nd->nd_ports_de = add_proc_file(nd, ports_entry_pointer,
&dgrp_ports_ops);
return 0;
/* FIXME this guy should free the tty driver stored in nd and destroy
* all channel ports */
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;
remove_files(nd);
dgrp_tty_uninit(nd);
ret = nd_struct_del(nd);
if (ret)
return ret;
kfree(nd);
return 0;
}
static struct proc_dir_entry *add_proc_file(struct nd_struct *node,
struct proc_dir_entry *root,
const struct file_operations *fops)
{
char buf[3];
ID_TO_CHAR(node->nd_ID, buf);
return proc_create_data(buf, 0600, root, fops, node);
}
/*
* 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/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_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)
{
if (sscanf(buf, "0x%x\n", &dgrp_poll_tick) != 1)
return -EINVAL;
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_register_with_sysfs.attr,
NULL
};
static struct attribute_group dgrp_global_settings_attribute_group = {
.name = NULL,
.attrs = dgrp_sysfs_global_settings_entries,
};
int dgrp_create_class_sysfs_files(void)
{
int ret = 0;
int max_majors = 1U << (32 - MINORBITS);
dgrp_class = class_create(THIS_MODULE, "digi_realport");
if (IS_ERR(dgrp_class))
return PTR_ERR(dgrp_class);
ret = class_create_file(dgrp_class, &class_attr_driver_version);
if (ret)
goto err_class;
dgrp_class_global_settings_dev = device_create(dgrp_class, NULL,
MKDEV(0, max_majors + 1), NULL, "driver_settings");
if (IS_ERR(dgrp_class_global_settings_dev)) {
ret = PTR_ERR(dgrp_class_global_settings_dev);
goto err_file;
}
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__);
goto err_dev1;
}
dgrp_class_nodes_dev = device_create(dgrp_class, NULL,
MKDEV(0, max_majors + 2), NULL, "nodes");
if (IS_ERR(dgrp_class_nodes_dev)) {
ret = PTR_ERR(dgrp_class_nodes_dev);
goto err_group;
}
return 0;
err_group:
sysfs_remove_group(&dgrp_class_global_settings_dev->kobj,
&dgrp_global_settings_attribute_group);
err_dev1:
device_destroy(dgrp_class, MKDEV(0, max_majors + 1));
err_file:
class_remove_file(dgrp_class, &class_attr_driver_version);
err_class:
class_destroy(dgrp_class);
return ret;
}
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 = 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 = dev_get_drvdata(c);
if (!nd)
return 0;
if (nd->nd_state == NS_READY)
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 = 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 = 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 = 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, "%s", 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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);
}
此差异已折叠。
/************************************************************************
* 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 */
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册