501 lines
13 KiB
C
501 lines
13 KiB
C
#ifdef __KERNEL__
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/string.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/bitops.h>
|
|
#include <linux/mutex.h>
|
|
|
|
#include "proc_api.h"
|
|
#include "../../common/uthash.h"
|
|
|
|
#define MAX_COMMAND_LEN (256)
|
|
#define MAX_CMD_LEN (32)
|
|
#define MAX_PARAMS_LEN (128)
|
|
|
|
#define TOLOWER(x) ((x) | 0x20)
|
|
#ifndef MAX
|
|
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
|
#endif
|
|
|
|
#define HELP_BITS (31)
|
|
#define IOCTL_BITS (30)
|
|
|
|
#define PROC_API_DBG_DIR ("api")
|
|
|
|
typedef struct {
|
|
char key[MAX_PATH]; ///< 节点名称
|
|
char dir_name[MAX_NAME_LEN]; ///< 目录名称
|
|
PDBGFS_PRIV data; ///< 节点数据
|
|
struct proc_dir_entry* entry; ///< proc 目录数据结构
|
|
UT_hash_handle hh; ///< Hash链表节点
|
|
} PROC_INFO, *PPROC_INFO;
|
|
|
|
static const struct file_operations g_DbgSeq_fops;
|
|
static struct proc_dir_entry* g_DebugProcFS;
|
|
|
|
static DEFINE_RWLOCK(g_proc_lock);
|
|
static PPROC_INFO g_proc_items = NULL;
|
|
|
|
static int proc_api_show(struct seq_file* seq, void* token)
|
|
{
|
|
PPROC_INFO pInfo = NULL, tmp = NULL;
|
|
int i = 0;
|
|
seq_printf(seq, "Total API items: %u\n", HASH_COUNT(g_proc_items));
|
|
seq_printf(seq, "API root direcotry: /proc/%s/\n", PROC_API_DIR_NAME);
|
|
seq_puts(seq, "-------------------------------------"
|
|
"-------------------------------------\n");
|
|
seq_puts(seq, "| ID | Direcotry | Name | Show | IOCtrl | Help |\n");
|
|
seq_puts(seq, "|------------------------------------"
|
|
"------------------------------------|\n");
|
|
|
|
read_lock(&g_proc_lock);
|
|
HASH_ITER(hh, g_proc_items, pInfo, tmp) {
|
|
seq_printf(seq, "| %03d | %14s | %14s | %1s | %1s | %1s |\n",
|
|
i++,
|
|
pInfo->dir_name ? pInfo->dir_name : "",
|
|
pInfo->data->name,
|
|
pInfo->data->show ? "*" : "-",
|
|
pInfo->data->ioctl ? "*" : "-",
|
|
pInfo->data->help ? "*" : "-");
|
|
}
|
|
read_unlock(&g_proc_lock);
|
|
|
|
seq_puts(seq, "-------------------------------------"
|
|
"-------------------------------------\n");
|
|
return 0;
|
|
}
|
|
|
|
static int proc_api_ioctl(struct seq_file* seq, void* token)
|
|
{
|
|
PDBGFS_PRIV priv = (PDBGFS_PRIV)(seq->private);
|
|
|
|
seq_printf(seq, "Run Command [%s] with (%s)\n", priv->cmd, priv->cmd_data);
|
|
return 0;
|
|
}
|
|
|
|
static int proc_api_help(struct seq_file* seq, void* token)
|
|
{
|
|
PDBGFS_PRIV priv = (PDBGFS_PRIV)(seq->private);
|
|
seq_puts(seq, "==============Options Helps=============\n");
|
|
seq_printf(seq, "usage: echo \"<params>\" > /proc/%s/%s/%s\n",
|
|
PROC_API_DIR_NAME, PROC_API_DBG_DIR, priv->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static DBGFS_PRIV g_DbgConfig[] = {
|
|
{
|
|
"info", 0, 0L, NULL, NULL,
|
|
proc_api_show, proc_api_ioctl, proc_api_help
|
|
},
|
|
};
|
|
|
|
static PROC_API g_ProcApi = {
|
|
PROC_API_DBG_DIR, g_DbgConfig, sizeof(g_DbgConfig) / sizeof(g_DbgConfig[0]),
|
|
};
|
|
|
|
static int proc_api_init(const char* proc_dir_name)
|
|
{
|
|
if(proc_dir_name) {
|
|
g_DebugProcFS = proc_mkdir(proc_dir_name, NULL);
|
|
|
|
if(g_DebugProcFS != NULL) {
|
|
return ERR_DBGFS_NO_ERROR;
|
|
}
|
|
}
|
|
|
|
return ERR_DBGFS_INIT;
|
|
}
|
|
|
|
static void proc_api_uninit(const char* proc_dir_name)
|
|
{
|
|
remove_proc_entry(proc_dir_name, NULL);
|
|
}
|
|
|
|
/**
|
|
* @brief 注册proc模块
|
|
*
|
|
* @param pv: 模块proc信息
|
|
* @return int: 0 成功, -EINVAL 参数错误
|
|
*/
|
|
int proc_api_register(PPROC_API pv)
|
|
{
|
|
struct proc_dir_entry* pdir;
|
|
int i = 0;
|
|
|
|
if(pv == NULL || pv->data == NULL || pv->num_data <= 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if(pv->dir_name && strlen(pv->dir_name) > 0) {
|
|
pdir = proc_mkdir(pv->dir_name, g_DebugProcFS);
|
|
} else {
|
|
pdir = g_DebugProcFS;
|
|
}
|
|
|
|
// printk(KERN_ERR "Register: %s number %d of %s\n", pv->dir_name, pv->num_data, pv->data->name);
|
|
|
|
for(i = 0; i < pv->num_data; i++) {
|
|
PPROC_INFO pInfo = (PPROC_INFO)kmalloc(sizeof(PROC_INFO), GFP_KERNEL);
|
|
|
|
if(pInfo == NULL) {
|
|
printk(KERN_ERR "Malloc Error\n");
|
|
return ERR_MALLOC_MEMORY;
|
|
}
|
|
|
|
memset(pInfo, 0, sizeof(PROC_INFO));
|
|
|
|
if(!proc_create_data(pv->data[i].name, 0x644, pdir, &g_DbgSeq_fops, &pv->data[i])) {
|
|
printk(KERN_ERR "proc_create_data Error\n");
|
|
continue;
|
|
}
|
|
|
|
if(pv->dir_name && strlen(pv->dir_name) > 0) {
|
|
strcpy(pInfo->dir_name, pv->dir_name);
|
|
sprintf(pInfo->key, "%s%s", pv->dir_name, pv->data[i].name);
|
|
} else {
|
|
sprintf(pInfo->key, "%s", pv->data[i].name);
|
|
}
|
|
|
|
pInfo->entry = pdir;
|
|
pInfo->data = &pv->data[i];
|
|
write_lock(&g_proc_lock);
|
|
HASH_ADD_STR(g_proc_items, key, pInfo);
|
|
write_unlock(&g_proc_lock);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief 清除proc模块注册
|
|
*
|
|
* @param pv: 模块proc信息
|
|
* @return int: 0 成功, -EINVAL 参数错误
|
|
*/
|
|
int proc_api_unregister(PPROC_API pv)
|
|
{
|
|
PPROC_INFO pInfo = NULL;
|
|
char key[MAX_PATH];
|
|
int i;
|
|
|
|
if(pv == NULL || pv->data == NULL || pv->num_data <= 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
for(i = 0; i < pv->num_data; i++) {
|
|
memset(key, 0, MAX_PATH);
|
|
|
|
if(pv->dir_name && strlen(pv->dir_name) > 0) {
|
|
sprintf(key, "%s%s", pv->dir_name, pv->data[i].name);
|
|
} else {
|
|
sprintf(key, "%s", pv->data[i].name);
|
|
}
|
|
|
|
HASH_FIND_STR(g_proc_items, key, pInfo);
|
|
|
|
if(pInfo) {
|
|
write_lock(&g_proc_lock);
|
|
HASH_DEL(g_proc_items, pInfo);
|
|
write_unlock(&g_proc_lock);
|
|
remove_proc_entry(pv->data[i].name, pInfo->entry);
|
|
kfree(pInfo);
|
|
}
|
|
}
|
|
|
|
if(pv->dir_name && strlen(pv->dir_name) > 0) {
|
|
remove_proc_subtree(pv->dir_name, g_DebugProcFS);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int proc_seq_open(struct inode* inode, struct file* file)
|
|
{
|
|
PDBGFS_PRIV priv = (PDBGFS_PRIV)(PDE_DATA(inode));
|
|
|
|
if(test_bit(HELP_BITS, &priv->mask) && priv->help) {
|
|
clear_bit(HELP_BITS, &priv->mask);
|
|
return single_open(file, priv->help, priv);
|
|
} else if(test_bit(IOCTL_BITS, &priv->mask) && priv->ioctl) {
|
|
clear_bit(IOCTL_BITS, &priv->mask);
|
|
return single_open(file, priv->ioctl, priv);
|
|
} else {
|
|
return single_open(file, priv->show, priv);
|
|
}
|
|
}
|
|
|
|
static int proc_seq_release(struct inode* inode, struct file* file)
|
|
{
|
|
return single_release(inode, file);
|
|
}
|
|
|
|
static int get_value_base(unsigned char* pBuf)
|
|
{
|
|
int strLen = pBuf ? strlen(pBuf) : -1;
|
|
|
|
if(pBuf) {
|
|
int i = 0;
|
|
|
|
if(pBuf[0] == '0' && pBuf[1] == 'x') {
|
|
return 16;
|
|
}
|
|
|
|
for(i = 0; i < strLen; i++) {
|
|
if(TOLOWER(pBuf[i]) >= 'a' && TOLOWER(pBuf[i]) <= 'f') {
|
|
return 16;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 10;
|
|
}
|
|
|
|
/**
|
|
* @brief 获取参数中的字符串数据类型值
|
|
*
|
|
* @param pBuf: 字符串缓冲区
|
|
* @param index: 指定参数位置
|
|
* @param pValue: 返回提取的字符串值
|
|
* @param maxBytes: 最大参数限制
|
|
* @return int: 0 成功, -EINVAL 参数错误,-ENOENT 没有对应的值
|
|
*/
|
|
int get_string_value(unsigned char* pBuf, int index, unsigned char* pValue, unsigned int maxBytes)
|
|
{
|
|
char tmpBuf[128];
|
|
char* token;
|
|
char* s = tmpBuf;
|
|
int paramIndex = 0;
|
|
|
|
if(pBuf == NULL || index < 0 || pValue == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(pValue, 0, maxBytes);
|
|
|
|
strcpy(tmpBuf, pBuf);
|
|
|
|
for(token = strsep(&s, " ");
|
|
token != NULL;
|
|
token = strsep(&s, " ")) {
|
|
if(token != NULL) {
|
|
if(index != paramIndex) {
|
|
paramIndex += 1;
|
|
continue;
|
|
}
|
|
|
|
strcpy(pValue, token);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
/**
|
|
* @brief 从参数的指定位置提取整数值
|
|
*
|
|
* @param pBuf: 字符串缓冲区
|
|
* @param index: 指定参数位置
|
|
* @param pValue: 返回获取的值
|
|
* @return int: 0 成功, -EINVAL 参数错误,-ENOENT 没有对应的值,-EFAULT 字符串转整数错误
|
|
*/
|
|
int get_int_value(unsigned char* pBuf, int index, int* pValue)
|
|
{
|
|
char tmpBuf[128];
|
|
char* token;
|
|
char* s = tmpBuf;
|
|
int paramIndex = 0;
|
|
int paramValue = -1;
|
|
|
|
if(pBuf == NULL || index < 0 || pValue == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
strcpy(tmpBuf, pBuf);
|
|
|
|
for(token = strsep(&s, " ");
|
|
token != NULL;
|
|
token = strsep(&s, " ")) {
|
|
if(token != NULL) {
|
|
if(index != paramIndex) {
|
|
paramIndex += 1;
|
|
continue;
|
|
}
|
|
|
|
if(kstrtol(token, get_value_base(token), (long*)¶mValue) != 0) {
|
|
printk(KERN_ERR "strict_strtol [%s] Error\n", token);
|
|
return -EFAULT;
|
|
} else {
|
|
*pValue = paramValue;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
static int process_input_content(struct seq_file* seq, unsigned char* pBuf, PDBGFS_PRIV pv)
|
|
{
|
|
int strLen = pBuf ? strlen(pBuf) - 1 : -1;
|
|
char tmpBuf[MAX_COMMAND_LEN], cmdBuf[MAX_CMD_LEN], paramBuf[MAX_PARAMS_LEN];
|
|
int i = 0;
|
|
int bIsCmd = 0;
|
|
|
|
if(strlen <= 0){
|
|
return -ERR_PARA_OUTOFRANGE;
|
|
}
|
|
|
|
memset(tmpBuf, 0, MAX_COMMAND_LEN);
|
|
memset(cmdBuf, 0, MAX_CMD_LEN);
|
|
memset(paramBuf, 0, MAX_PARAMS_LEN);
|
|
|
|
if(pBuf == NULL && pv == NULL) {
|
|
return -ERR_PARA_OUTOFRANGE;
|
|
}
|
|
|
|
strcpy(tmpBuf, pBuf);
|
|
|
|
for(i = 0; i < strLen; i++) {
|
|
if(tmpBuf[i] == ' ') {
|
|
strncpy(cmdBuf, tmpBuf, i);
|
|
strncpy(paramBuf, &tmpBuf[i + 1], MAX(strLen - i - 1, 0));
|
|
bIsCmd = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!bIsCmd) {
|
|
if(kstrtoul(pBuf, get_value_base(pBuf), (long unsigned int*)&pv->params)) {
|
|
printk(KERN_ERR "strict_strtoul [%s] base %d Error\n", pBuf, get_value_base(pBuf));
|
|
return -ERR_DBGFS_WRITEOPTS;
|
|
}
|
|
} else {
|
|
if(pv->ioctl != NULL) {
|
|
if(pv->cmd != NULL) {
|
|
kfree(pv->cmd);
|
|
}
|
|
|
|
if(pv->cmd_data) {
|
|
kfree(pv->cmd_data);
|
|
}
|
|
|
|
if(strlen(cmdBuf) > 0) {
|
|
pv->cmd = kmalloc(strlen(cmdBuf) + 1, GFP_KERNEL);
|
|
|
|
if(pv->cmd == NULL) {
|
|
printk(KERN_ERR "Malloc %u Error\n", strlen(cmdBuf) + 1);
|
|
return -ERR_MALLOC_MEMORY;
|
|
}
|
|
|
|
strcpy(pv->cmd, cmdBuf);
|
|
}
|
|
|
|
if(strlen(paramBuf) > 0) {
|
|
pv->cmd_data = kmalloc(strlen(paramBuf) + 1, GFP_KERNEL);
|
|
|
|
if(pv->cmd_data == NULL) {
|
|
printk(KERN_ERR "Malloc %u Error\n", strlen(paramBuf) + 1);
|
|
return -ERR_MALLOC_MEMORY;
|
|
}
|
|
|
|
strcpy(pv->cmd_data, paramBuf);
|
|
}
|
|
|
|
set_bit(IOCTL_BITS, &pv->mask);
|
|
printk(KERN_INFO "%s do %s command with %s\n", pv->name, pv->cmd, pv->cmd_data);
|
|
}
|
|
|
|
return ERR_DBGFS_NO_ERROR;
|
|
}
|
|
|
|
return ERR_PARA_OUTOFRANGE;
|
|
}
|
|
|
|
static ssize_t proc_seq_option_write(struct file* file, const char __user* userbuf,
|
|
size_t count, loff_t* data)
|
|
{
|
|
char buf[64];
|
|
struct seq_file* seq = (struct seq_file*)file->private_data;
|
|
PDBGFS_PRIV pv = (PDBGFS_PRIV)seq->private;
|
|
|
|
if(count >= sizeof(buf)) {
|
|
printk(KERN_ERR "Input Params Error:count = %d, max size = %d\n", count,
|
|
sizeof(buf));
|
|
return -ERR_PARA_OUTOFRANGE;
|
|
}
|
|
|
|
if(copy_from_user(buf, userbuf, count)) {
|
|
printk(KERN_ERR "Copy Data To Kernel Error\n");
|
|
return -ERR_DBGFS_WRITEOPTS;
|
|
}
|
|
|
|
if(buf[0] == 'h' &&
|
|
buf[1] == 'e' &&
|
|
buf[2] == 'l' &&
|
|
buf[3] == 'p') {
|
|
if(pv->help != NULL) {
|
|
set_bit(HELP_BITS, &pv->mask);
|
|
//pv->help((struct seq_file*)file->private_data, pv);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
buf[count] = 0x00;
|
|
|
|
if(!process_input_content(seq, buf, pv) == ERR_DBGFS_NO_ERROR) {
|
|
printk(KERN_ERR "Input [%s] Process Error\n", buf);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static const struct file_operations g_DbgSeq_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = proc_seq_open,
|
|
.read = seq_read,
|
|
.write = proc_seq_option_write,
|
|
.llseek = seq_lseek,
|
|
.release = proc_seq_release,
|
|
};
|
|
|
|
#define VERSION ("v0.0.0.2")
|
|
|
|
static int api_module_init(void)
|
|
{
|
|
printk(KERN_ALERT "Hello ISG PROC API version: %s\n", VERSION);
|
|
proc_api_init(PROC_API_DIR_NAME);
|
|
|
|
if(proc_api_register(&g_ProcApi) != ERR_DBGFS_NO_ERROR) {
|
|
printk(KERN_ERR "Regisetr %s Error\n", g_ProcApi.dir_name);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
module_init(api_module_init);
|
|
|
|
static void __exit api_module_exit(void)
|
|
{
|
|
printk(KERN_ALERT "Bye ISG PROC API version: %s\n", VERSION);
|
|
proc_api_unregister(&g_ProcApi);
|
|
|
|
proc_api_uninit(PROC_API_DIR_NAME);
|
|
}
|
|
module_exit(api_module_exit);
|
|
|
|
EXPORT_SYMBOL(proc_api_register);
|
|
EXPORT_SYMBOL(proc_api_unregister);
|
|
EXPORT_SYMBOL(get_int_value);
|
|
EXPORT_SYMBOL(get_string_value);
|
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
MODULE_VERSION("0.1");
|
|
#endif
|