#ifdef __KERNEL__ #include #include #include #include #include #include #include #include #include #include #include "proc_api.h" #include "../../common/uthash.h" #include "../../common/common.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->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 \"\" > /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 %lu 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 %ld 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 = %ld, max size = %ld\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 __init api_module_init(void) { printk(KERN_INFO "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_INFO "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