blob: 3b388494e2afd80dc7c2ce66f84d2bccff153f5a [file] [log] [blame]
/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
* All rights reserved.
*
* 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 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*/
/** @file *********************************************************************
*
* Handle procfs-specific tasks.
* Note that this file does not know about any module-specific things, nor
* does it know anything about what information to reveal as part of the proc
* entries. The 2 functions that take care of displaying device and
* driver specific information are passed as parameters to
* visor_easyproc_InitDriver().
*
* void show_device_info(struct seq_file *seq, void *p);
* void show_driver_info(struct seq_file *seq);
*
* The second parameter to show_device_info is actually a pointer to the
* device-specific info to show. It is the context that was originally
* passed to visor_easyproc_InitDevice().
*
******************************************************************************
*/
#include <linux/proc_fs.h>
#include "uniklog.h"
#include "timskmod.h"
#include "easyproc.h"
#define MYDRVNAME "easyproc"
/*
* /proc/<ProcId> ProcDir
* /proc/<ProcId>/driver ProcDriverDir
* /proc/<ProcId>/driver/diag ProcDriverDiagFile
* /proc/<ProcId>/device ProcDeviceDir
* /proc/<ProcId>/device/0 procDevicexDir
* /proc/<ProcId>/device/0/diag procDevicexDiagFile
*/
static ssize_t proc_write_device(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos);
static ssize_t proc_write_driver(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos);
static struct proc_dir_entry *
createProcDir(char *name, struct proc_dir_entry *parent)
{
struct proc_dir_entry *p = proc_mkdir_mode(name, S_IFDIR, parent);
if (p == NULL)
ERRDRV("failed to create /proc directory %s", name);
return p;
}
static int seq_show_driver(struct seq_file *seq, void *offset);
static int proc_open_driver(struct inode *inode, struct file *file)
{
return single_open(file, seq_show_driver, PDE_DATA(inode));
}
static const struct file_operations proc_fops_driver = {
.open = proc_open_driver,
.read = seq_read,
.write = proc_write_driver,
.llseek = seq_lseek,
.release = single_release,
};
static int seq_show_device(struct seq_file *seq, void *offset);
static int seq_show_device_property(struct seq_file *seq, void *offset);
static int proc_open_device(struct inode *inode, struct file *file)
{
return single_open(file, seq_show_device, PDE_DATA(inode));
}
static const struct file_operations proc_fops_device = {
.open = proc_open_device,
.read = seq_read,
.write = proc_write_device,
.llseek = seq_lseek,
.release = single_release,
};
static int proc_open_device_property(struct inode *inode, struct file *file)
{
return single_open(file, seq_show_device_property, PDE_DATA(inode));
}
static const struct file_operations proc_fops_device_property = {
.open = proc_open_device_property,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void visor_easyproc_InitDriver(struct easyproc_driver_info *pdriver,
char *procId,
void (*show_driver_info)(struct seq_file *),
void (*show_device_info)(struct seq_file *,
void *))
{
memset(pdriver, 0, sizeof(struct easyproc_driver_info));
pdriver->ProcId = procId;
if (pdriver->ProcId == NULL)
ERRDRV("ProcId cannot be NULL (trouble ahead)!");
pdriver->Show_driver_info = show_driver_info;
pdriver->Show_device_info = show_device_info;
if (pdriver->ProcDir == NULL)
pdriver->ProcDir = createProcDir(pdriver->ProcId, NULL);
if ((pdriver->ProcDir != NULL) && (pdriver->ProcDriverDir == NULL))
pdriver->ProcDriverDir = createProcDir("driver",
pdriver->ProcDir);
if ((pdriver->ProcDir != NULL) && (pdriver->ProcDeviceDir == NULL))
pdriver->ProcDeviceDir = createProcDir("device",
pdriver->ProcDir);
if ((pdriver->ProcDriverDir != NULL) &&
(pdriver->ProcDriverDiagFile == NULL)) {
pdriver->ProcDriverDiagFile =
proc_create_data("diag", 0,
pdriver->ProcDriverDir,
&proc_fops_driver, pdriver);
if (pdriver->ProcDriverDiagFile == NULL)
ERRDRV("failed to register /proc/%s/driver/diag entry",
pdriver->ProcId);
}
}
EXPORT_SYMBOL_GPL(visor_easyproc_InitDriver);
void visor_easyproc_InitDriverEx(struct easyproc_driver_info *pdriver,
char *procId,
void (*show_driver_info)(struct seq_file *),
void (*show_device_info)(struct seq_file *,
void *),
void (*write_driver_info)(char *buf,
size_t count,
loff_t *ppos),
void (*write_device_info)(char *buf,
size_t count,
loff_t *ppos,
void *p))
{
visor_easyproc_InitDriver(pdriver, procId,
show_driver_info, show_device_info);
pdriver->Write_driver_info = write_driver_info;
pdriver->Write_device_info = write_device_info;
}
EXPORT_SYMBOL_GPL(visor_easyproc_InitDriverEx);
void visor_easyproc_DeInitDriver(struct easyproc_driver_info *pdriver)
{
if (pdriver->ProcDriverDiagFile != NULL) {
remove_proc_entry("diag", pdriver->ProcDriverDir);
pdriver->ProcDriverDiagFile = NULL;
}
if (pdriver->ProcDriverDir != NULL) {
remove_proc_entry("driver", pdriver->ProcDir);
pdriver->ProcDriverDir = NULL;
}
if (pdriver->ProcDeviceDir != NULL) {
remove_proc_entry("device", pdriver->ProcDir);
pdriver->ProcDeviceDir = NULL;
}
if (pdriver->ProcDir != NULL) {
remove_proc_entry(pdriver->ProcId, NULL);
pdriver->ProcDir = NULL;
}
pdriver->ProcId = NULL;
pdriver->Show_driver_info = NULL;
pdriver->Show_device_info = NULL;
pdriver->Write_driver_info = NULL;
pdriver->Write_device_info = NULL;
}
EXPORT_SYMBOL_GPL(visor_easyproc_DeInitDriver);
void visor_easyproc_InitDevice(struct easyproc_driver_info *pdriver,
struct easyproc_device_info *p, int devno,
void *devdata)
{
if ((pdriver->ProcDeviceDir != NULL) && (p->procDevicexDir == NULL)) {
char s[29];
sprintf(s, "%d", devno);
p->procDevicexDir = createProcDir(s, pdriver->ProcDeviceDir);
p->devno = devno;
}
p->devdata = devdata;
p->pdriver = pdriver;
p->devno = devno;
if ((p->procDevicexDir != NULL) && (p->procDevicexDiagFile == NULL)) {
p->procDevicexDiagFile =
proc_create_data("diag", 0, p->procDevicexDir,
&proc_fops_device, p);
if (p->procDevicexDiagFile == NULL)
ERRDEVX(devno, "failed to register /proc/%s/device/%d/diag entry",
pdriver->ProcId, devno
);
}
memset(&(p->device_property_info[0]), 0,
sizeof(p->device_property_info));
}
EXPORT_SYMBOL_GPL(visor_easyproc_InitDevice);
void visor_easyproc_CreateDeviceProperty(struct easyproc_device_info *p,
void (*show_property_info)
(struct seq_file *, void *),
char *property_name)
{
size_t i;
struct easyproc_device_property_info *px = NULL;
if (p->procDevicexDir == NULL) {
ERRDRV("state error");
return;
}
for (i = 0; i < ARRAY_SIZE(p->device_property_info); i++) {
if (p->device_property_info[i].procEntry == NULL) {
px = &(p->device_property_info[i]);
break;
}
}
if (!px) {
ERRDEVX(p->devno, "too many device properties");
return;
}
px->devdata = p->devdata;
px->pdriver = p->pdriver;
px->procEntry = proc_create_data(property_name, 0, p->procDevicexDir,
&proc_fops_device_property, px);
if (strlen(property_name)+1 > sizeof(px->property_name)) {
ERRDEVX(p->devno, "device property name %s too long",
property_name);
return;
}
strcpy(px->property_name, property_name);
if (px->procEntry == NULL) {
ERRDEVX(p->devno, "failed to register /proc/%s/device/%d/%s entry",
p->pdriver->ProcId, p->devno, property_name
);
return;
}
px->show_device_property_info = show_property_info;
}
EXPORT_SYMBOL_GPL(visor_easyproc_CreateDeviceProperty);
void visor_easyproc_DeInitDevice(struct easyproc_driver_info *pdriver,
struct easyproc_device_info *p, int devno)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(p->device_property_info); i++) {
if (p->device_property_info[i].procEntry != NULL) {
struct easyproc_device_property_info *px =
&(p->device_property_info[i]);
remove_proc_entry(px->property_name, p->procDevicexDir);
px->procEntry = NULL;
}
}
if (p->procDevicexDiagFile != NULL) {
remove_proc_entry("diag", p->procDevicexDir);
p->procDevicexDiagFile = NULL;
}
if (p->procDevicexDir != NULL) {
char s[29];
sprintf(s, "%d", devno);
remove_proc_entry(s, pdriver->ProcDeviceDir);
p->procDevicexDir = NULL;
}
p->devdata = NULL;
p->pdriver = NULL;
}
EXPORT_SYMBOL_GPL(visor_easyproc_DeInitDevice);
static int seq_show_driver(struct seq_file *seq, void *offset)
{
struct easyproc_driver_info *p =
(struct easyproc_driver_info *)(seq->private);
if (!p)
return 0;
(*(p->Show_driver_info))(seq);
return 0;
}
static int seq_show_device(struct seq_file *seq, void *offset)
{
struct easyproc_device_info *p =
(struct easyproc_device_info *)(seq->private);
if ((!p) || (!(p->pdriver)))
return 0;
(*(p->pdriver->Show_device_info))(seq, p->devdata);
return 0;
}
static int seq_show_device_property(struct seq_file *seq, void *offset)
{
struct easyproc_device_property_info *p =
(struct easyproc_device_property_info *)(seq->private);
if ((!p) || (!(p->show_device_property_info)))
return 0;
(*(p->show_device_property_info))(seq, p->devdata);
return 0;
}
static ssize_t proc_write_driver(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct seq_file *seq = (struct seq_file *)file->private_data;
struct easyproc_driver_info *p = NULL;
char local_buf[256];
if (seq == NULL)
return 0;
p = (struct easyproc_driver_info *)(seq->private);
if ((!p) || (!(p->Write_driver_info)))
return 0;
if (count >= sizeof(local_buf))
return -ENOMEM;
if (copy_from_user(local_buf, buffer, count))
return -EFAULT;
local_buf[count] = '\0'; /* be friendly */
(*(p->Write_driver_info))(local_buf, count, ppos);
return count;
}
static ssize_t proc_write_device(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct seq_file *seq = (struct seq_file *)file->private_data;
struct easyproc_device_info *p = NULL;
char local_buf[256];
if (seq == NULL)
return 0;
p = (struct easyproc_device_info *)(seq->private);
if ((!p) || (!(p->pdriver)) || (!(p->pdriver->Write_device_info)))
return 0;
if (count >= sizeof(local_buf))
return -ENOMEM;
if (copy_from_user(local_buf, buffer, count))
return -EFAULT;
local_buf[count] = '\0'; /* be friendly */
(*(p->pdriver->Write_device_info))(local_buf, count, ppos, p->devdata);
return count;
}