diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-01 19:26:52 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-01 19:26:52 (GMT) |
commit | 3498d13b8090c0b0ef911409fbc503a7c4cca6ef (patch) | |
tree | 254ca00276e863d9fba25707690c66b2a04c49e9 /drivers/staging/dgrp/dgrp_mon_ops.c | |
parent | def7cb8cd4e3258db88050eaaca5438bcc3dafca (diff) | |
parent | 0c57dfcc6c1d037243c2f8fbf62eab3633326ec0 (diff) | |
download | linux-fsl-qoriq-3498d13b8090c0b0ef911409fbc503a7c4cca6ef.tar.xz |
Merge tag 'tty-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull TTY changes from Greg Kroah-Hartman:
"As we skipped the merge window for 3.6-rc1 for the tty tree,
everything is now settled down and working properly, so we are ready
for 3.7-rc1. Here's the patchset, it's big, but the large changes are
removing a firmware file and adding a staging tty driver (it depended
on the tty core changes, so it's going through this tree instead of
the staging tree.)
All of these patches have been in the linux-next tree for a while.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
Fix up more-or-less trivial conflicts in
- drivers/char/pcmcia/synclink_cs.c:
tty NULL dereference fix vs tty_port_cts_enabled() helper function
- drivers/staging/{Kconfig,Makefile}:
add-add conflict (dgrp driver added close to other staging drivers)
- drivers/staging/ipack/devices/ipoctal.c:
"split ipoctal_channel from iopctal" vs "TTY: use tty_port_register_device"
* tag 'tty-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (235 commits)
tty/serial: Add kgdb_nmi driver
tty/serial/amba-pl011: Quiesce interrupts in poll_get_char
tty/serial/amba-pl011: Implement poll_init callback
tty/serial/core: Introduce poll_init callback
kdb: Turn KGDB_KDB=n stubs into static inlines
kdb: Implement disable_nmi command
kernel/debug: Mask KGDB NMI upon entry
serial: pl011: handle corruption at high clock speeds
serial: sccnxp: Make 'default' choice in switch last
serial: sccnxp: Remove mask termios caps for SW flow control
serial: sccnxp: Report actual baudrate back to core
serial: samsung: Add poll_get_char & poll_put_char
Powerpc 8xx CPM_UART setting MAXIDL register proportionaly to baud rate
Powerpc 8xx CPM_UART maxidl should not depend on fifo size
Powerpc 8xx CPM_UART too many interrupts
Powerpc 8xx CPM_UART desynchronisation
serial: set correct baud_base for EXSYS EX-41092 Dual 16950
serial: omap: fix the reciever line error case
8250: blacklist Winbond CIR port
8250_pnp: do pnp probe before legacy probe
...
Diffstat (limited to 'drivers/staging/dgrp/dgrp_mon_ops.c')
-rw-r--r-- | drivers/staging/dgrp/dgrp_mon_ops.c | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/drivers/staging/dgrp/dgrp_mon_ops.c b/drivers/staging/dgrp/dgrp_mon_ops.c new file mode 100644 index 0000000..268dcb9 --- /dev/null +++ b/drivers/staging/dgrp/dgrp_mon_ops.c @@ -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; +} |