/*
|
* Copyright (c) 2015 South Silicon Valley Microelectronics Inc.
|
* Copyright (c) 2015 iComm Corporation
|
*
|
* 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 3 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.
|
* See the GNU General Public License for more details.
|
* You should have received a copy of the GNU General Public License
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
*/
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#include <linux/kernel.h>
|
#include <linux/version.h>
|
#include <linux/module.h>
|
#include <linux/moduleparam.h>
|
#include <linux/slab.h>
|
#include <linux/proc_fs.h>
|
#include <linux/uaccess.h>
|
#include <linux/errno.h>
|
#ifdef CONFIG_DEBUG_FS
|
#include <linux/debugfs.h>
|
#endif
|
#include "ssv_cmd.h"
|
#include "ssv_cfg.h"
|
#include <linux/fs.h>
|
#include <asm/segment.h>
|
#include <linux/uaccess.h>
|
#include <linux/buffer_head.h>
|
#include <linux/ctype.h>
|
MODULE_AUTHOR("iComm Semiconductor Co., Ltd");
|
MODULE_DESCRIPTION("Shared library for SSV wireless LAN cards.");
|
MODULE_LICENSE("Dual BSD/GPL");
|
static char *stacfgpath = NULL;
|
EXPORT_SYMBOL(stacfgpath);
|
module_param(stacfgpath, charp, 0000);
|
MODULE_PARM_DESC(stacfgpath, "Get path of sta cfg");
|
char *cfgfirmwarepath = NULL;
|
EXPORT_SYMBOL(cfgfirmwarepath);
|
module_param(cfgfirmwarepath, charp, 0000);
|
MODULE_PARM_DESC(cfgfirmwarepath, "Get firmware path");
|
char* ssv_initmac = NULL;
|
EXPORT_SYMBOL(ssv_initmac);
|
module_param(ssv_initmac, charp, 0644);
|
MODULE_PARM_DESC(ssv_initmac, "Wi-Fi MAC address");
|
u32 ssv_devicetype = 0;
|
EXPORT_SYMBOL(ssv_devicetype);
|
#ifdef CONFIG_DEBUG_FS
|
static struct dentry *debugfs;
|
#endif
|
struct proc_dir_entry *procfs;
|
static char *ssv6xxx_cmd_buf;
|
char *ssv6xxx_result_buf;
|
extern struct ssv6xxx_cfg_cmd_table cfg_cmds[];
|
extern struct ssv6xxx_cfg ssv_cfg;
|
char DEFAULT_CFG_PATH[] = "/vendor/etc/firmware/ssv6051-wifi.cfg";
|
static int ssv6xxx_dbg_open(struct inode *inode, struct file *filp)
|
{
|
filp->private_data = inode->i_private;
|
return 0;
|
}
|
static ssize_t ssv6xxx_dbg_read(struct file *filp, char __user *buffer,
|
size_t count, loff_t *ppos)
|
{
|
int len;
|
if (*ppos != 0)
|
return 0;
|
len = strlen(ssv6xxx_result_buf) + 1;
|
if (len == 1)
|
return 0;
|
if (copy_to_user(buffer, ssv6xxx_result_buf, len))
|
return -EFAULT;
|
ssv6xxx_result_buf[0] = 0x00;
|
return len;
|
}
|
static ssize_t ssv6xxx_dbg_write(struct file *filp, const char __user *buffer,
|
size_t count, loff_t *ppos)
|
{
|
if (*ppos != 0 || count > 255)
|
return 0;
|
if (copy_from_user(ssv6xxx_cmd_buf, buffer, count))
|
return -EFAULT;
|
ssv6xxx_cmd_buf[count-1] = 0x00;
|
ssv_cmd_submit(ssv6xxx_cmd_buf);
|
return count;
|
}
|
size_t read_line(struct file *fp, char *buf, size_t size)
|
{
|
size_t num_read = 0;
|
size_t total_read = 0;
|
char *buffer;
|
char ch;
|
size_t start_ignore = 0;
|
if (size <= 0 || buf == NULL) {
|
total_read = -EINVAL;
|
return -EINVAL;
|
}
|
buffer = buf;
|
for (;;) {
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,37)
|
if (fp->f_op && fp->f_op->read)
|
num_read = fp->f_op->read(fp, &ch, 1, &fp->f_pos);
|
#else
|
num_read = vfs_read(fp, &ch, 1, &fp->f_pos);
|
#endif
|
if (num_read < 0) {
|
if (num_read == EINTR)
|
continue;
|
else
|
return -1;
|
}
|
else if (num_read == 0) {
|
if (total_read == 0)
|
return 0;
|
else
|
break;
|
}
|
else {
|
if (ch == '#')
|
start_ignore = 1;
|
if (total_read < size - 1) {
|
total_read++;
|
if (start_ignore)
|
*buffer++ = '\0';
|
else
|
*buffer++ = ch;
|
}
|
if (ch == '\n')
|
break;
|
}
|
}
|
*buffer = '\0';
|
return total_read;
|
}
|
int ischar(char *c)
|
{
|
int is_char = 1;
|
while(*c) {
|
if (isalpha(*c) || isdigit(*c) || *c == '_' || *c == ':' || *c == '/' || *c == '.' || *c == '-')
|
c++;
|
else {
|
is_char = 0;
|
break;
|
}
|
}
|
return is_char;
|
}
|
void sta_cfg_set(char *stacfgpath)
|
{
|
struct file *fp = (struct file *) NULL;
|
char buf[MAX_CHARS_PER_LINE], cfg_cmd[32], cfg_value[32];
|
mm_segment_t fs;
|
size_t s, read_len = 0, is_cmd_support = 0;
|
printk("\n*** %s, %s ***\n\n", __func__, stacfgpath);
|
if (stacfgpath == NULL) {
|
stacfgpath = DEFAULT_CFG_PATH;
|
printk("redirect to %s\n", stacfgpath);
|
}
|
memset(&ssv_cfg, 0, sizeof(ssv_cfg));
|
memset(buf, 0, sizeof(buf));
|
fp = filp_open(stacfgpath, O_RDONLY, 0);
|
if (IS_ERR(fp) || fp == NULL) {
|
printk("ERROR: filp_open\n");
|
WARN_ON(1);
|
return;
|
}
|
if (fp->f_path.dentry == NULL) {
|
printk("ERROR: dentry NULL\n");
|
WARN_ON(1);
|
return;
|
}
|
do {
|
memset(cfg_cmd, '\0', sizeof(cfg_cmd));
|
memset(cfg_value, '\0', sizeof(cfg_value));
|
fs = get_fs();
|
set_fs(KERNEL_DS);
|
read_len = read_line(fp, buf, MAX_CHARS_PER_LINE);
|
set_fs(fs);
|
sscanf(buf, "%s = %s", cfg_cmd, cfg_value);
|
if (!ischar(cfg_cmd) || !ischar(cfg_value)) {
|
printk("ERORR invalid parameter: %s\n", buf);
|
WARN_ON(1);
|
continue;
|
}
|
is_cmd_support = 0;
|
for(s=0; cfg_cmds[s].cfg_cmd != NULL; s++) {
|
if (strcmp(cfg_cmds[s].cfg_cmd, cfg_cmd)==0) {
|
cfg_cmds[s].translate_func(cfg_value,
|
cfg_cmds[s].var, cfg_cmds[s].arg);
|
is_cmd_support = 1;
|
break;
|
}
|
}
|
if (!is_cmd_support && strlen(cfg_cmd) > 0) {
|
printk("ERROR Unsupported command: %s", cfg_cmd);
|
WARN_ON(1);
|
}
|
} while (read_len > 0);
|
filp_close(fp, NULL);
|
}
|
static struct file_operations ssv6xxx_dbg_fops = {
|
.owner = THIS_MODULE,
|
.open = ssv6xxx_dbg_open,
|
.read = ssv6xxx_dbg_read,
|
.write = ssv6xxx_dbg_write,
|
};
|
#if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
|
extern int ssv6xxx_hci_init(void);
|
extern void ssv6xxx_hci_exit(void);
|
extern int ssv6xxx_init(void);
|
extern void ssv6xxx_exit(void);
|
extern int ssv6xxx_sdio_init(void);
|
extern void ssv6xxx_sdio_exit(void);
|
#endif
|
#if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
|
int ssvdevice_init(void)
|
#else
|
static int __init ssvdevice_init(void)
|
#endif
|
{
|
ssv6xxx_cmd_buf = (char *)kzalloc(CLI_BUFFER_SIZE+CLI_RESULT_BUF_SIZE, GFP_KERNEL);
|
if (!ssv6xxx_cmd_buf)
|
return -ENOMEM;
|
ssv6xxx_result_buf = ssv6xxx_cmd_buf+CLI_BUFFER_SIZE;
|
ssv6xxx_cmd_buf[0] = 0x00;
|
ssv6xxx_result_buf[0] = 0x00;
|
#ifdef CONFIG_DEBUG_FS
|
debugfs = debugfs_create_dir(DEBUG_DIR_ENTRY,
|
NULL);
|
if (!debugfs)
|
return -ENOMEM;
|
debugfs_create_u32(DEBUG_DEVICETYPE_ENTRY, S_IRUGO|S_IWUSR, debugfs, &ssv_devicetype);
|
debugfs_create_file(DEBUG_CMD_ENTRY, S_IRUGO|S_IWUSR, debugfs, NULL, &ssv6xxx_dbg_fops);
|
#endif
|
procfs = proc_mkdir(DEBUG_DIR_ENTRY, NULL);
|
if (!procfs)
|
return -ENOMEM;
|
proc_create(DEBUG_CMD_ENTRY, S_IRUGO|S_IWUGO, procfs, &ssv6xxx_dbg_fops);
|
sta_cfg_set(stacfgpath);
|
#if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
|
{
|
int ret;
|
ret = ssv6xxx_hci_init();
|
if(!ret){
|
ret = ssv6xxx_init();
|
}if(!ret){
|
ret = ssv6xxx_sdio_init();
|
}
|
return ret;
|
}
|
#endif
|
return 0;
|
}
|
#if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
|
void ssvdevice_exit(void)
|
#else
|
static void __exit ssvdevice_exit(void)
|
#endif
|
{
|
#if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
|
ssv6xxx_exit();
|
ssv6xxx_hci_exit();
|
ssv6xxx_sdio_exit();
|
#endif
|
#ifdef CONFIG_DEBUG_FS
|
debugfs_remove_recursive(debugfs);
|
#endif
|
remove_proc_entry(DEBUG_CMD_ENTRY, procfs);
|
remove_proc_entry(DEBUG_DIR_ENTRY, NULL);
|
kfree(ssv6xxx_cmd_buf);
|
}
|
#if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
|
EXPORT_SYMBOL(ssvdevice_init);
|
EXPORT_SYMBOL(ssvdevice_exit);
|
#else
|
module_init(ssvdevice_init);
|
module_exit(ssvdevice_exit);
|
module_param_named(devicetype,ssv_devicetype, uint , S_IRUSR | S_IWUSR);
|
MODULE_PARM_DESC(devicetype, "Enable sdio bridge Mode/Wifi Mode.");
|
#endif
|