blob: 3ad5da1935967538caf27627248301405600548f [file] [log] [blame]
/*
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "sw.h"
#include "sw_api.h"
#ifdef KVER26 /*Linux Kernel 2.6 */
#define __USER __user
#else /*Linux Kernel 2.4 */
#include <asm/uaccess.h>
#define __USER
#define CLONE_KERNEL (CLONE_FS | CLONE_FILES | CLONE_SIGHAND)
#define for_each_process(p) for_each_task(p)
#endif /*KVER26 */
#include <net/sock.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/version.h>
#include "api_access.h"
#include "sw_api_ks.h"
#if 0
#define dprintk(args...) aos_printk(args)
#else
#define dprintk(args...)
#endif
/*configurable value for max creating request of kernel thread*/
#define PID_THREADS_MAX 32
#define RSV_PID_LOC_0 (0)
#define RSV_PID_LOC_1 (1)
#define PID_TAB_MAX PID_THREADS_MAX
#define PID_TAB_NOT_FOUND PID_TAB_MAX+1
static pid_t pid_parents[PID_TAB_MAX] = {0};
static pid_t pid_childs[PID_TAB_MAX] = {0};
static wait_queue_head_t pid_child_wait[PID_TAB_MAX];
static struct semaphore pid_tab_sem;
static unsigned long *cmd_buf = NULL;
static struct semaphore api_sem;
static struct sock *ssdk_nl_sk = NULL;
static struct sk_buff * skb_array[PID_TAB_MAX] = {0};
static sw_error_t
input_parser(sw_api_param_t *p, a_uint32_t nr_param, a_uint32_t *args)
{
a_uint32_t i = 0, buf_head = nr_param;
for (i = 0; i < nr_param; i++)
{
if (p->param_type & SW_PARAM_PTR)
{
cmd_buf[i] = (a_uint32_t) & cmd_buf[buf_head];
buf_head += (p->data_size + 3) / 4;
if (buf_head > (SW_MAX_API_BUF / 4))
return SW_NO_RESOURCE;
if (p->param_type & SW_PARAM_IN)
{
if (copy_from_user((a_uint8_t*)(cmd_buf[i]), (void __USER *)args[i + 2], ((p->data_size + 3) >> 2) << 2))
return SW_NO_RESOURCE;
}
}
else
{
cmd_buf[i] = args[i + 2];
}
p++;
}
return SW_OK;
}
static sw_error_t
output_parser(sw_api_param_t *p, a_uint32_t nr_param, a_uint32_t *args)
{
a_uint32_t i =0;
for (i = 0; i < nr_param; i++)
{
if (p->param_type & SW_PARAM_OUT)
{
if (copy_to_user
((void __USER *) args[i + 2], (a_uint32_t *) cmd_buf[i], ((p->data_size + 3) >> 2) << 2))
return SW_NO_RESOURCE;
}
p++;
}
return SW_OK;
}
static sw_error_t
sw_api_cmd(a_uint32_t * args)
{
a_uint32_t *p = cmd_buf, api_id = args[0], nr_param = 0;
sw_error_t(*func) (a_uint32_t, ...);
sw_api_param_t *pp;
sw_api_func_t *fp;
sw_error_t rv;
sw_api_t sw_api;
down(&api_sem);
sw_api.api_id = api_id;
rv = sw_api_get(&sw_api);
SW_OUT_ON_ERROR(rv);
fp = sw_api.api_fp;
pp = sw_api.api_pp;
nr_param = sw_api.api_nr;
rv = input_parser(pp, nr_param, args);
SW_OUT_ON_ERROR(rv);
func = fp->func;
switch (nr_param)
{
case 1:
rv = (func) (p[0]);
break;
case 2:
rv = (func) (p[0], p[1]);
break;
case 3:
rv = (func) (p[0], p[1], p[2]);
break;
case 4:
rv = (func) (p[0], p[1], p[2], p[3]);
break;
case 5:
rv = (func) (p[0], p[1], p[2], p[3], p[4]);
break;
case 6:
rv = (func) (p[0], p[1], p[2], p[3], p[4], p[5]);
break;
case 7:
rv = (func) (p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
break;
case 8:
rv = (func) (p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
break;
case 9:
rv = (func) (p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]);
break;
case 10:
rv = (func) (p[0], p[1], p[2], p[3], p[4], p[5],
p[6], p[7], p[8], p[9]);
break;
default:
rv = SW_OUT_OF_RANGE;
}
SW_OUT_ON_ERROR(rv);
rv = output_parser(pp, nr_param, args);
out:
up(&api_sem);
return rv;
}
static inline int pid_find(pid_t pid, pid_t pids[])
{
a_uint32_t i, loc = PID_TAB_NOT_FOUND;
for(i = 0; i< PID_TAB_MAX; i++)
{
if(pids[i] == pid)
{
loc = i;
break;
}
}
return loc;
}
static inline a_bool_t pid_exit(pid_t parent_pid)
{
struct task_struct *p;
a_bool_t rtn = A_TRUE;
for_each_process(p)
{
if(parent_pid == p->pid)
{
rtn = A_FALSE;
break;
}
}
return rtn;
}
static inline void pid_free(a_uint32_t loc)
{
if (down_interruptible(&pid_tab_sem))
return;
pid_childs[loc] = 0;
pid_parents[loc] = 0;
up(&pid_tab_sem);
}
static inline a_bool_t pid_full(void)
{
return (pid_find(0, pid_parents) == PID_TAB_NOT_FOUND)?A_TRUE:A_FALSE;
}
static a_uint32_t pid_find_save (pid_t parent_pid, pid_t child_pid)
{
a_uint32_t loc = PID_TAB_NOT_FOUND;
if(!parent_pid && !child_pid)
{
dprintk("child and father can't both zero\n");
return loc;
}
if (down_interruptible(&pid_tab_sem))
return loc;
if(!parent_pid)
{
/*find locate by child_pid*/
loc = pid_find(child_pid, pid_childs);
}
else
{
/*find locate by parent_pid*/
loc = pid_find(parent_pid, pid_parents);
if(child_pid)
{
loc = pid_find(0, pid_parents);
if(loc != PID_TAB_NOT_FOUND)
{
pid_childs[loc] = child_pid;
pid_parents[loc] = parent_pid;
}
}
}
up(&pid_tab_sem);
return loc;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
static void
sw_api_excep_ack(struct sock *sk, pid_t pid)
{
sw_error_t rv = SW_NO_RESOURCE;
a_uint32_t args[SW_MAX_API_PARAM], rtn;
struct sk_buff *skb, *skb_first = NULL;
struct nlmsghdr *nlh = NULL;
while(1)
{
#ifdef KVER26
skb = skb_dequeue(&sk->sk_receive_queue);
#else
skb = skb_dequeue(&sk->receive_queue);
#endif
if (!skb)
{
dprintk("pid error: skb = null\n");
return;
}
nlh = (struct nlmsghdr *)skb->data;
if (!nlh)
{
dprintk("pid error: nlh = null\n");
return;
}
if(nlh->nlmsg_pid == pid)
{
break;
}
if(!skb_first)
{
skb_first = skb;
}
else if (skb_first == skb)
{
dprintk("can't found my skb???\n");
return;
}
#ifdef KVER26
skb_queue_tail(&sk->sk_receive_queue, skb);
#else
skb_queue_tail(&sk->receive_queue, skb);
#endif
}
if(nlh->nlmsg_len < (SW_MAX_PAYLOAD + sizeof(nlmsghdr)))
{
dprintk("data length is less than %d bytes\n", SW_MAX_PAYLOAD);
SW_OUT_ON_ERROR(SW_ABORTED);
}
aos_mem_copy(args, NLMSG_DATA(nlh), SW_MAX_PAYLOAD);
/* return API result to user */
rtn = (a_uint32_t) rv;
if (copy_to_user
((void __USER *) args[1], (a_uint32_t *) & rtn, sizeof (a_uint32_t)))
{
rv = SW_NO_RESOURCE;
}
NETLINK_CB(skb).pid = 0;
NETLINK_CB(skb).dst_pid = nlh->nlmsg_pid;
#ifdef KVER26
NETLINK_CB(skb).dst_group = 0;
#else
NETLINK_CB(skb).dst_groups = 0;
#endif
netlink_unicast(sk, skb, nlh->nlmsg_pid, MSG_DONTWAIT);
}
static void
sw_api_exec(struct sock *sk, pid_t pid)
{
sw_error_t rv = SW_NO_RESOURCE;
a_uint32_t args[SW_MAX_API_PARAM], rtn;
struct sk_buff *skb, *skb_first = NULL;
struct nlmsghdr *nlh = NULL;
while(1)
{
#ifdef KVER26
skb = skb_dequeue(&sk->sk_receive_queue);
#else
skb = skb_dequeue(&sk->receive_queue);
#endif
if (!skb)
{
dprintk("pid error: skb = null\n");
return;
}
nlh = (struct nlmsghdr *)skb->data;
if (!nlh)
{
dprintk("pid error: nlh = null\n");
return;
}
if(nlh->nlmsg_pid == pid)
{
break;
}
if(!skb_first)
{
skb_first = skb;
}
else if (skb_first == skb)
{
dprintk("can't found my skb???\n");
return;
}
#ifdef KVER26
skb_queue_tail(&sk->sk_receive_queue, skb);
#else
skb_queue_tail(&sk->receive_queue, skb);
#endif
}
if(nlh->nlmsg_len < (SW_MAX_PAYLOAD + sizeof(nlmsghdr)))
{
dprintk("data length is less than %d bytes\n", SW_MAX_PAYLOAD);
SW_OUT_ON_ERROR(SW_ABORTED);
}
aos_mem_copy(args, NLMSG_DATA(nlh), SW_MAX_PAYLOAD);
rv = sw_api_cmd(args);
/* return API result to user */
rtn = (a_uint32_t) rv;
if (copy_to_user
((void __USER *) args[1], (a_uint32_t *) & rtn, sizeof (a_uint32_t)))
{
rv = SW_NO_RESOURCE;
}
NETLINK_CB(skb).pid = 0;
NETLINK_CB(skb).dst_pid = nlh->nlmsg_pid;
#ifdef KVER26
NETLINK_CB(skb).dst_group = 0;
#else
NETLINK_CB(skb).dst_groups = 0;
#endif
netlink_unicast(sk, skb, nlh->nlmsg_pid, MSG_DONTWAIT);
}
static int sw_api_thread(void *sk)
{
a_uint32_t loc, i;
pid_t parent_pid = 0, child_pid = current->pid;
while ((loc = pid_find_save(parent_pid, child_pid)) == PID_TAB_NOT_FOUND)
schedule_timeout(1*HZ);
parent_pid = pid_parents[loc];
dprintk("thread child [%d] find parent [%d] at %d \n", child_pid, parent_pid, loc);
if ((RSV_PID_LOC_0 == loc) || (RSV_PID_LOC_1 == loc))
{
for(i=0; ; i++)
{
if(i && !sleep_on_timeout(&pid_child_wait[loc], (5*HZ)))
{
if(pid_exit(parent_pid) == A_FALSE)
continue;
pid_free(loc);
dprintk("thread child[%d] exit!\n", child_pid);
return 0;
}
sw_api_exec(sk, parent_pid);
}
}
else
{
sw_api_exec(sk, parent_pid);
pid_free(loc);
}
return 0;
}
static void
sw_api_netlink(struct sock *sk, int len)
{
pid_t parent_pid = current->pid, child_pid = 0;
a_uint32_t loc = pid_find_save (parent_pid, child_pid);
if(loc == PID_TAB_NOT_FOUND)
{
if(pid_full())
{
dprintk("###threads exceed the max [%d] for pid [%d]!###\n", PID_TAB_MAX, parent_pid);
sw_api_excep_ack(sk, parent_pid);
return;
}
#if 1
struct task_struct *p;
p = kthread_create(sw_api_thread, (void *)ssdk_nl_sk, "netlink_child");
if (IS_ERR(p))
{
dprintk("thread can't be created for netlink\n");
return;
}
child_pid = p->pid;
#else
if ((child_pid = kernel_thread(sw_api_thread, ssdk_nl_sk, CLONE_KERNEL)) < 0)
{
dprintk("thread can't be created for netlink\n");
return;
}
#endif
dprintk("[%d] create child [%d] at %d\n", parent_pid, child_pid, loc);
pid_find_save(parent_pid, child_pid);
wake_up_process(p);
}
else
{
dprintk("[%d] wake up child [%d] at %d\n", parent_pid, pid_childs[loc], loc);
wake_up(&pid_child_wait[loc]);
}
return;
}
#else
static void
sw_api_excep_ack_26_22(struct sk_buff *skb)
{
sw_error_t rv = SW_NO_RESOURCE;
a_uint32_t args[SW_MAX_API_PARAM], rtn, size, dst_pid;
struct nlmsghdr *nlh = NULL;
struct sk_buff *rep;
nlh = (struct nlmsghdr *)skb->data;
if (!nlh)
{
dprintk("pid error: nlh = null\n");
return;
}
dst_pid = nlh->nlmsg_pid;
if(nlh->nlmsg_len < (SW_MAX_PAYLOAD + sizeof(nlmsghdr)))
{
dprintk("data length is less than %d bytes\n", SW_MAX_PAYLOAD);
SW_OUT_ON_ERROR(SW_ABORTED);
}
aos_mem_copy(args, NLMSG_DATA(nlh), SW_MAX_PAYLOAD);
/* return API result to user */
rtn = (a_uint32_t) rv;
if (copy_to_user
((void __USER *) args[1], (a_uint32_t *) & rtn, sizeof (a_uint32_t)))
{
rv = SW_NO_RESOURCE;
}
size = NLMSG_SPACE(0);
rep = alloc_skb(size, GFP_ATOMIC);
if (!rep)
{
dprintk("reply socket buffer allocation error... \n");
return;
}
nlh = nlmsg_put(rep, 0, 0, 0, 0, 0);
NETLINK_CB(rep).pid = 0;
NETLINK_CB(rep).dst_group = 0;
netlink_unicast(ssdk_nl_sk, rep, dst_pid, MSG_DONTWAIT);
}
static void
sw_api_exec_26_22(pid_t parent_pid)
{
sw_error_t rv = SW_NO_RESOURCE;
a_uint32_t loc, args[SW_MAX_API_PARAM], rtn, skblen, nlmsglen, size, dst_pid;
struct nlmsghdr *nlh = NULL;
struct sk_buff *skb;
struct sk_buff *rep;
loc = pid_find(parent_pid, pid_parents);
if (PID_TAB_NOT_FOUND == loc)
{
dprintk("parent PID not found - (%d)\n", parent_pid);
return;
}
skb = skb_array[loc];
if (!skb)
{
dprintk("skb null pointer error\n");
return;
}
skblen = skb->len;
if (skb->len < sizeof(nlh))
{
dprintk("skb len error - (%d)\n", skb->len);
SW_OUT_ON_ERROR(SW_ABORTED);
}
nlh = (struct nlmsghdr *)skb->data;
if (!nlh)
{
dprintk("pid error: nlh = null\n");
SW_OUT_ON_ERROR(SW_ABORTED);
}
nlmsglen = nlh->nlmsg_len;
if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen)
{
dprintk("nlmsglen error - (%d)\n", nlmsglen);
SW_OUT_ON_ERROR(SW_ABORTED);
}
dst_pid = nlh->nlmsg_pid;
if(nlmsglen < (SW_MAX_PAYLOAD + sizeof(nlmsghdr)))
{
dprintk("data length is less than %d bytes\n", SW_MAX_PAYLOAD);
SW_OUT_ON_ERROR(SW_ABORTED);
}
aos_mem_copy(args, NLMSG_DATA(nlh), SW_MAX_PAYLOAD);
rv = sw_api_cmd(args);
/* return API result to user */
rtn = (a_uint32_t) rv;
if (copy_to_user
((void __USER *) args[1], (a_uint32_t *) & rtn, sizeof (a_uint32_t)))
{
rv = SW_NO_RESOURCE;
}
size = NLMSG_SPACE(0);
rep = alloc_skb(size, GFP_ATOMIC);
if (!rep)
{
dprintk("reply socket buffer allocation error... \n");
SW_OUT_ON_ERROR(SW_ABORTED);
}
nlh = nlmsg_put(rep, 0, 0, 0, 0, 0);
NETLINK_CB(rep).pid = 0;
NETLINK_CB(rep).dst_group = 0;
netlink_unicast(ssdk_nl_sk, rep, dst_pid, MSG_DONTWAIT);
out:
skb_array[loc] = NULL;
kfree_skb(skb);
}
static int
sw_api_thread_26_22(void * data)
{
a_uint32_t loc, i;
pid_t parent_pid = 0, child_pid = current->pid;
while ((loc = pid_find_save(parent_pid, child_pid)) == PID_TAB_NOT_FOUND)
schedule_timeout(1*HZ);
parent_pid = pid_parents[loc];
dprintk("thread child [%d] find parent [%d] at %d \n", child_pid, parent_pid, loc);
if ((RSV_PID_LOC_0 == loc) || (RSV_PID_LOC_1 == loc))
{
for(i=0; ; i++)
{
if(i && !sleep_on_timeout(&pid_child_wait[loc], (5*HZ)))
{
if(pid_exit(parent_pid) == A_FALSE)
continue;
pid_free(loc);
dprintk("thread child[%d] exit!\n", child_pid);
return 0;
}
sw_api_exec_26_22(parent_pid);
}
}
else
{
sw_api_exec_26_22(parent_pid);
pid_free(loc);
}
return 0;
}
static void
sw_api_netlink_26_22(struct sk_buff *skb)
{
pid_t parent_pid = current->pid, child_pid = 0;
a_uint32_t loc = pid_find_save (parent_pid, child_pid);
if(loc == PID_TAB_NOT_FOUND)
{
if(pid_full())
{
dprintk("###threads exceed the max [%d] for pid [%d]!###\n", PID_TAB_MAX, parent_pid);
sw_api_excep_ack_26_22(skb);
return;
}
loc = pid_find_save(parent_pid, 0xffffffff);
#if 1
struct task_struct *p;
p = kthread_create(sw_api_thread_26_22, (void *)ssdk_nl_sk, "netlink_child");
if (IS_ERR(p))
{
dprintk("thread can't be created for netlink\n");
return;
}
skb_array[loc] = skb_get(skb);
child_pid = p->pid;
pid_childs[loc] = child_pid;
wake_up_process(p);
#else
if ((child_pid = kernel_thread(sw_api_thread_26_22, NULL, CLONE_KERNEL)) < 0)
{
dprintk("thread can't be created for netlink\n");
return;
}
#endif
dprintk("[%d] create child [%d] at %d\n", parent_pid, child_pid, loc);
}
else
{
dprintk("[%d] wake up child [%d] at %d\n", parent_pid, pid_childs[loc], loc);
skb_array[loc] = skb_get(skb);
wake_up(&pid_child_wait[loc]);
}
return;
}
#endif
sw_error_t
sw_uk_init(a_uint32_t nl_prot)
{
a_uint32_t i, protocol;
if (!cmd_buf)
{
if((cmd_buf = (a_uint32_t *) aos_mem_alloc(SW_MAX_API_BUF)) == NULL)
return SW_OUT_OF_MEM;
}
if (!ssdk_nl_sk)
{
#if defined UK_NL_PROT
protocol = UK_NL_PROT;
#else
protocol = nl_prot;
#endif
#ifdef KVER26
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
ssdk_nl_sk = netlink_kernel_create(&init_net, protocol, 0, sw_api_netlink_26_22, NULL, THIS_MODULE);
#else
ssdk_nl_sk = netlink_kernel_create(protocol, 0, sw_api_netlink, THIS_MODULE);
#endif
#else
ssdk_nl_sk = netlink_kernel_create(protocol, sw_api_netlink);
#endif
if (!ssdk_nl_sk)
{
dprintk("netlink_kernel_create fail at nl_prot:[%d]\n", protocol);
return SW_NO_RESOURCE;
}
else
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
dprintk("netlink_kernel_create succeeded at nl_prot: [%d] (>2.6.22)\n", protocol);
#else
dprintk("netlink_kernel_create succeeded at nl_prot: [%d] (<2.6.22)\n", protocol);
#endif
}
}
init_MUTEX(&pid_tab_sem);
init_MUTEX(&api_sem);
for(i = 0; i < PID_TAB_MAX; i++)
{
init_waitqueue_head(&pid_child_wait[i]);
}
return SW_OK;
}
sw_error_t
sw_uk_cleanup(void)
{
if (cmd_buf)
{
aos_mem_free(cmd_buf);
cmd_buf = NULL;
}
if (ssdk_nl_sk)
{
#ifdef KVER26
sock_release(ssdk_nl_sk->sk_socket);
#else
sock_release(ssdk_nl_sk->socket);
#endif
ssdk_nl_sk = NULL;
}
return SW_OK;
}