/*
* 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/>.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <net/sock.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/version.h>
#include <ssv6200.h>
#include "lib.h"
#include "dev.h"
#define NETLINK_SMARTLINK (31)
#define MAX_PAYLOAD (2048)
static struct sock *nl_sk = NULL;
struct ssv_softc *ssv_smartlink_sc = NULL;
EXPORT_SYMBOL(ssv_smartlink_sc);
u32 ssv_smartlink_status=0;
static int _ksmartlink_start_smartlink(u8 *pInBuf, u32 inBufLen, u8 *pOutBuf, u32 *pOutBufLen)
{
#ifdef KSMARTLINK_DEBUG
printk(KERN_INFO "%s\n", __FUNCTION__);
#endif
ssv_smartlink_status = 1;
*pOutBufLen = 0;
return 0;
}
int ksmartlink_smartlink_started(void)
{
return ssv_smartlink_status;
}
EXPORT_SYMBOL(ksmartlink_smartlink_started);
static int _ksmartlink_stop_smartlink(u8 *pInBuf, u32 inBufLen, u8 *pOutBuf, u32 *pOutBufLen)
{
#ifdef KSMARTLINK_DEBUG
printk(KERN_INFO "%s\n", __FUNCTION__);
#endif
ssv_smartlink_status = 0;
*pOutBufLen = 0;
return 0;
}
static int _ksmartlink_set_channel(u8 *pInBuf, u32 inBufLen, u8 *pOutBuf, u32 *pOutBufLen)
{
int ret=-10;
int ch=(int)(*pInBuf);
struct ssv_softc *sc=ssv_smartlink_sc;
#ifdef KSMARTLINK_DEBUG
printk(KERN_INFO "%s %d\n", __FUNCTION__, ch);
#endif
if (!sc)
{
goto out;
}
mutex_lock(&sc->mutex);
ret = ssv6xxx_set_channel(sc, ch);
mutex_unlock(&sc->mutex);
*pOutBufLen = 0;
out:
return ret;
}
static int _ksmartlink_get_channel(u8 *pInBuf, u32 inBufLen, u8 *pOutBuf, u32 *pOutBufLen)
{
int ret=-10;
int ch=0;
struct ssv_softc *sc=ssv_smartlink_sc;
#ifdef KSMARTLINK_DEBUG
printk(KERN_INFO "%s\n", __FUNCTION__);
#endif
if (!sc)
{
goto out;
}
mutex_lock(&sc->mutex);
ret = ssv6xxx_get_channel(sc, &ch);
mutex_unlock(&sc->mutex);
*pOutBuf = ch;
*pOutBufLen = 1;
#ifdef KSMARTLINK_DEBUG
printk(KERN_INFO "%s %d\n", __FUNCTION__, ch);
#endif
out:
return ret;
}
static int _ksmartlink_set_promisc(u8 *pInBuf, u32 inBufLen, u8 *pOutBuf, u32 *pOutBufLen)
{
int ret=-10;
int accept=(int)(*pInBuf);
struct ssv_softc *sc=ssv_smartlink_sc;
#ifdef KSMARTLINK_DEBUG
printk(KERN_INFO "%s %d\n", __FUNCTION__, accept);
#endif
if (!sc)
{
goto out;
}
mutex_lock(&sc->mutex);
ret = ssv6xxx_set_promisc(sc, accept);
mutex_unlock(&sc->mutex);
*pOutBufLen = 0;
out:
return ret;
}
static int _ksmartlink_get_promisc(u8 *pInBuf, u32 inBufLen, u8 *pOutBuf, u32 *pOutBufLen)
{
int ret=-10;
int accept=(int)(*pInBuf);
struct ssv_softc *sc=ssv_smartlink_sc;
#ifdef KSMARTLINK_DEBUG
printk(KERN_INFO "%s\n", __FUNCTION__);
#endif
if (!sc)
{
goto out;
}
mutex_lock(&sc->mutex);
ret = ssv6xxx_get_promisc(sc, &accept);
mutex_unlock(&sc->mutex);
*pOutBuf = accept;
*pOutBufLen = 1;
#ifdef KSMARTLINK_DEBUG
printk(KERN_INFO "%s %d\n", __FUNCTION__, accept);
#endif
out:
return ret;
}
#define SMARTLINK_CMD_FIXED_LEN (10)
#define SMARTLINK_CMD_FIXED_TOT_LEN (SMARTLINK_CMD_FIXED_LEN+1)
#define SMARTLINK_RES_FIXED_LEN (SMARTLINK_CMD_FIXED_LEN)
#define SMARTLINK_RES_FIXED_TOT_LEN (SMARTLINK_RES_FIXED_LEN+2)
struct ksmartlink_cmd
{
char *cmd;
int (*process_func)(u8 *, u32, u8 *, u32 *);
};
static struct ksmartlink_cmd _ksmartlink_cmd_table[] =
{
{"startairki", _ksmartlink_start_smartlink},
{"stopairkis", _ksmartlink_stop_smartlink},
{"setchannel", _ksmartlink_set_channel},
{"getchannel", _ksmartlink_get_channel},
{"setpromisc", _ksmartlink_set_promisc},
{"getpromisc", _ksmartlink_get_promisc},
};
static u32 _ksmartlink_cmd_table_size=sizeof(_ksmartlink_cmd_table)/sizeof(struct ksmartlink_cmd);
#ifdef KSMARTLINK_DEBUG
static void _ksmartlink_hex_dump(u8 *pInBuf, u32 inBufLen)
{
u32 i=0;
printk(KERN_INFO "\nKernel Hex Dump(len=%d):\n", inBufLen);
printk(KERN_INFO ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
for (i=0; i<inBufLen; i++)
{
if ((i) && ((i & 0xf) == 0))
{
printk("\n");
}
printk("%02x ", pInBuf[i]);
}
printk(KERN_INFO "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
}
#endif
static int _ksmartlink_process_msg(u8 *pInBuf, u32 inBufLen, u8 *pOutBuf, u32 *pOutBufLen)
{
int ret=0;
u32 i=0;
struct ksmartlink_cmd *pCmd;
if (!pInBuf || !pOutBuf || !pOutBufLen)
{
printk(KERN_ERR "NULL pointer\n");
return -1;
}
for (i=0; i<_ksmartlink_cmd_table_size; i++)
{
if (!strncmp(_ksmartlink_cmd_table[i].cmd, pInBuf, SMARTLINK_CMD_FIXED_LEN))
{
break;
}
}
if (i < _ksmartlink_cmd_table_size)
{
pCmd = &_ksmartlink_cmd_table[i];
if (!pCmd->process_func)
{
printk(KERN_ERR "CMD %s has NULL process_func\n", pCmd->cmd);
return -3;
}
ret = pCmd->process_func(pInBuf+SMARTLINK_CMD_FIXED_LEN, inBufLen, pOutBuf, pOutBufLen);
#ifdef CONFIG_SSV_NETLINK_RESPONSE
if (ret < 0)
{
*pOutBufLen = SMARTLINK_RES_FIXED_TOT_LEN;
}
else
{
if (*pOutBufLen > 0)
{
pOutBuf[SMARTLINK_RES_FIXED_LEN] = (u8)ret;
pOutBuf[SMARTLINK_RES_FIXED_LEN+1]= *pOutBuf;
}
else
{
pOutBuf[SMARTLINK_RES_FIXED_LEN] = (u8)ret;
pOutBuf[SMARTLINK_RES_FIXED_LEN+1]= 0;
}
*pOutBufLen = SMARTLINK_RES_FIXED_TOT_LEN;
}
memcpy(pOutBuf, pCmd->cmd, SMARTLINK_RES_FIXED_LEN);
#else
(void)pOutBuf;
(void)pOutBufLen;
#endif
return 0;
}
else
{
printk(KERN_INFO "Unknow CMD or Packet?\n");
}
return 0;
}
static u8 gkBuf[MAX_PAYLOAD]={0};
static int ssv_usr_pid=0;
void smartlink_nl_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
#ifdef CONFIG_SSV_NETLINK_RESPONSE
struct sk_buff *skb_out;
#endif
int ret=0;
u8 *pInBuf=NULL;
u32 inBufLen=0;
u32 outBufLen=0;
nlh = (struct nlmsghdr *)skb->data;
ssv_usr_pid = nlh->nlmsg_pid;
pInBuf = (u8 *)nlmsg_data(nlh);
inBufLen = nlmsg_len(nlh);
#ifdef KSMARTLINK_DEBUG
_ksmartlink_hex_dump(pInBuf, inBufLen);
#endif
outBufLen = 0;
memset(gkBuf, 0, MAX_PAYLOAD);
ret = _ksmartlink_process_msg(pInBuf, inBufLen, gkBuf, &outBufLen);
#ifdef CONFIG_SSV_NETLINK_RESPONSE
if (outBufLen == 0)
{
memcpy(gkBuf, "Nothing", 8);
outBufLen = strlen(gkBuf);
}
skb_out = nlmsg_new(outBufLen, 0);
if (!skb_out)
{
printk(KERN_ERR "Failed to allocate new skb\n");
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, outBufLen, 0);
NETLINK_CB(skb_out).dst_group = 0;
memcpy(nlmsg_data(nlh), gkBuf, outBufLen);
ret = nlmsg_unicast(nl_sk, skb_out, ssv_usr_pid);
if (ret < 0)
{
printk(KERN_ERR "Error while sending bak to user\n");
}
#endif
return;
}
void smartlink_nl_send_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
struct sk_buff *skb_out;
int ret=0;
u8 *pOutBuf=skb->data;
u32 outBufLen=skb->len;
#ifdef KSMARTLINK_DEBUG
#endif
skb_out = nlmsg_new(outBufLen, 0);
if (!skb_out)
{
printk(KERN_ERR "Allocate new skb failed!\n");
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, outBufLen, 0);
NETLINK_CB(skb_out).dst_group = 0;
memcpy(nlmsg_data(nlh), pOutBuf, outBufLen);
ret = nlmsg_unicast(nl_sk, skb_out, ssv_usr_pid);
if (ret < 0)
{
printk(KERN_ERR "nlmsg_unicast failed!\n");
}
kfree_skb(skb);
return;
}
EXPORT_SYMBOL(smartlink_nl_send_msg);
int ksmartlink_init(void)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)
nl_sk = netlink_kernel_create(&init_net,
NETLINK_SMARTLINK,
0,
smartlink_nl_recv_msg,
NULL,
THIS_MODULE);
#else
struct netlink_kernel_cfg cfg =
{
.groups = 0,
.input = smartlink_nl_recv_msg,
};
nl_sk = netlink_kernel_create(&init_net,
NETLINK_SMARTLINK,
&cfg);
#endif
printk(KERN_INFO "***************SmartLink Init-S**************\n");
if(!nl_sk)
{
printk(KERN_ERR "Error creating socket.\n");
return -10;
}
printk(KERN_INFO "***************SmartLink Init-E**************\n");
return 0;
}
void ksmartlink_exit(void)
{
printk(KERN_INFO "%s\n", __FUNCTION__);
if (nl_sk)
{
netlink_kernel_release(nl_sk);
nl_sk = NULL;
}
}
#if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
EXPORT_SYMBOL(ksmartlink_init);
EXPORT_SYMBOL(ksmartlink_exit);
#else
module_init(ksmartlink_init);
module_exit(ksmartlink_exit);
#endif