mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 08:19:28 +00:00
![David Howells](/assets/img/avatar_default.png)
Include missing linux/slab.h inclusions where the source file is currently expecting to get kmalloc() and co. through linux/proc_fs.h. Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> cc: linux-s390@vger.kernel.org cc: sparclinux@vger.kernel.org cc: linux-efi@vger.kernel.org cc: linux-mtd@lists.infradead.org cc: devel@driverdev.osuosl.org cc: x86@kernel.org Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
541 lines
12 KiB
C
541 lines
12 KiB
C
/*
|
|
*
|
|
* Copyright 1999 Digi International (www.digi.com)
|
|
* James Puzzo <jamesp at digi dot com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* Filename:
|
|
*
|
|
* dgrp_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);
|
|
}
|