diff options
Diffstat (limited to 'drivers/staging/dgrp/dgrp_common.c')
-rw-r--r-- | drivers/staging/dgrp/dgrp_common.c | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/drivers/staging/dgrp/dgrp_common.c b/drivers/staging/dgrp/dgrp_common.c new file mode 100644 index 000000000000..3553998b72bc --- /dev/null +++ b/drivers/staging/dgrp/dgrp_common.c @@ -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 (!uid_eq(GLOBAL_ROOT_UID, current_euid())) + mode >>= 6; + else if (in_egroup_p(GLOBAL_ROOT_GID)) + 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); +} |