Index: lede/feeds/packages/net/miniupnpd/patches/302-add_wsc_new_file.patch
===================================================================
--- /dev/null
+++ lede/feeds/packages/net/miniupnpd/patches/302-add_wsc_new_file.patch
@@ -0,0 +1,6539 @@
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/README
+===================================================================
+--- /dev/null
++++ b/wsc/README
+@@ -0,0 +1,9 @@
++Mediatek WPS(WiFi Portected Setup) miniupnpd supported
++
++Please firstly refer to "MediatekWPSMiniUPnPDaemonHowTo.doc". 
++
++Release Notes
++--------
++2.3.0:
++	1. Initial verison.
++ 
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/Version
+===================================================================
+--- /dev/null
++++ b/wsc/Version
+@@ -0,0 +1 @@
++2.3.0
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/sample_util.c
+===================================================================
+--- /dev/null
++++ b/wsc/sample_util.c
+@@ -0,0 +1,292 @@
++///////////////////////////////////////////////////////////////////////////
++//
++// Copyright (c) 2000-2003 Intel Corporation 
++// All rights reserved. 
++//
++// Redistribution and use in source and binary forms, with or without 
++// modification, are permitted provided that the following conditions are met: 
++//
++// * Redistributions of source code must retain the above copyright notice, 
++// this list of conditions and the following disclaimer. 
++// * Redistributions in binary form must reproduce the above copyright notice, 
++// this list of conditions and the following disclaimer in the documentation 
++// and/or other materials provided with the distribution. 
++// * Neither name of Intel Corporation nor the names of its contributors 
++// may be used to endorse or promote products derived from this software 
++// without specific prior written permission.
++// 
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 
++// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
++// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
++// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
++// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
++// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++//
++///////////////////////////////////////////////////////////////////////////
++
++#include <stdarg.h>
++#include "sample_util.h"
++
++/********************************************************************************
++ * SampleUtil_GetElementValue
++ *
++ * Description: 
++ *       Given a DOM node such as <Channel>11</Channel>, this routine
++ *       extracts the value (e.g., 11) from the node and returns it as 
++ *       a string. The string must be freed by the caller using 
++ *       free.
++ *
++ * Parameters:
++ *   node -- The DOM node from which to extract the value
++ *
++ ********************************************************************************/
++
++char *
++SampleUtil_GetElementValue( IN IXML_Element * element )
++{
++
++    IXML_Node *child = ixmlNode_getFirstChild( ( IXML_Node * ) element );
++
++    char *temp = NULL;
++
++    if( ( child != 0 ) && ( ixmlNode_getNodeType( child ) == eTEXT_NODE ) ) {
++        temp = strdup( ixmlNode_getNodeValue( child ) );
++    }
++
++    return temp;
++}
++
++
++/********************************************************************************
++ * SampleUtil_GetFirstServiceList
++ *
++ * Description: 
++ *       Given a DOM node representing a UPnP Device Description Document,
++ *       this routine parses the document and finds the first service list
++ *       (i.e., the service list for the root device).  The service list
++ *       is returned as a DOM node list.
++ *
++ * Parameters:
++ *   node -- The DOM node from which to extract the service list
++ *
++ ********************************************************************************/
++IXML_NodeList *
++SampleUtil_GetFirstServiceList(
++	IN IXML_Document * doc)
++{
++    IXML_NodeList *ServiceList = NULL;
++    IXML_NodeList *servlistnodelist = NULL;
++    IXML_Node *servlistnode = NULL;
++
++    servlistnodelist = ixmlDocument_getElementsByTagName(doc, "serviceList");
++    if (servlistnodelist && ixmlNodeList_length(servlistnodelist))
++	{
++
++        /*
++           we only care about the first service list, from the root device 
++         */
++		servlistnode = ixmlNodeList_item(servlistnodelist, 0);
++
++        /*
++           create as list of DOM nodes 
++         */
++		ServiceList = ixmlElement_getElementsByTagName((IXML_Element *)servlistnode, "service");
++    }
++
++	if (servlistnodelist)
++		ixmlNodeList_free(servlistnodelist);
++
++    return ServiceList;
++}
++
++
++/********************************************************************************
++ * SampleUtil_GetFirstDocumentItem
++ *
++ * Description: 
++ *       Given a document node, this routine searches for the first element
++ *       named by the input string item, and returns its value as a string.
++ *       String must be freed by caller using free.
++ * Parameters:
++ *   doc -- The DOM document from which to extract the value
++ *   item -- The item to search for
++ *
++ ********************************************************************************/
++char *
++SampleUtil_GetFirstDocumentItem(
++	IN IXML_Document * doc,
++	IN const char *item)
++{
++    IXML_NodeList *nodeList = NULL;
++    IXML_Node *textNode = NULL;
++    IXML_Node *tmpNode = NULL;
++
++    char *ret = NULL;
++
++    nodeList = ixmlDocument_getElementsByTagName( doc, ( char * )item );
++
++    if( nodeList ) {
++        if( ( tmpNode = ixmlNodeList_item( nodeList, 0 ) ) ) {
++            textNode = ixmlNode_getFirstChild( tmpNode );
++
++            ret = strdup( ixmlNode_getNodeValue( textNode ) );
++        }
++    }
++
++    if( nodeList )
++        ixmlNodeList_free( nodeList );
++    return ret;
++}
++
++
++/********************************************************************************
++ * SampleUtil_GetFirstElementItem
++ *
++ * Description: 
++ *       Given a DOM element, this routine searches for the first element
++ *       named by the input string item, and returns its value as a string.
++ *       The string must be freed using free.
++ * Parameters:
++ *   node -- The DOM element from which to extract the value
++ *   item -- The item to search for
++ *
++ ********************************************************************************/
++char *
++SampleUtil_GetFirstElementItem(
++	IN IXML_Element * element,
++	IN const char *item)
++{
++    IXML_NodeList *nodeList = NULL;
++    IXML_Node *textNode = NULL;
++    IXML_Node *tmpNode = NULL;
++
++    char *ret = NULL;
++
++    nodeList = ixmlElement_getElementsByTagName( element, ( char * )item );
++
++    if( nodeList == NULL ) {
++        return NULL;
++    }
++
++    if( ( tmpNode = ixmlNodeList_item( nodeList, 0 ) ) == NULL ) {
++        ixmlNodeList_free( nodeList );
++        return NULL;
++    }
++
++    textNode = ixmlNode_getFirstChild( tmpNode );
++
++    ret = strdup( ixmlNode_getNodeValue( textNode ) );
++
++    if( !ret ) {
++        ixmlNodeList_free( nodeList );
++        return NULL;
++    }
++
++    ixmlNodeList_free( nodeList );
++
++    return ret;
++}
++
++
++/********************************************************************************
++ * SampleUtil_FindAndParseService
++ *
++ * Description: 
++ *       This routine finds the first occurance of a service in a DOM representation
++ *       of a description document and parses it.  
++ *
++ * Parameters:
++ *   DescDoc -- The DOM description document
++ *   location -- The location of the description document
++ *   serviceSearchType -- The type of service to search for
++ *   serviceId -- OUT -- The service ID
++ *   eventURL -- OUT -- The event URL for the service
++ *   controlURL -- OUT -- The control URL for the service
++ *
++ ********************************************************************************/
++int
++SampleUtil_FindAndParseService(
++	IN IXML_Document * DescDoc,
++                                IN char *location,
++                                IN char *serviceType,
++                                OUT char **serviceId,
++	OUT char **SCPDURL,
++                                OUT char **eventURL,
++	OUT char **controlURL)
++{
++    int i, length, found = 0;
++    int ret;
++    char *tempServiceType = NULL;
++    char *baseURL = NULL;
++    char *base;
++    char *relcontrolURL = NULL, *releventURL = NULL, *relSCPDURL = NULL;
++    IXML_NodeList *serviceList = NULL;
++    IXML_Element *service = NULL;
++
++    baseURL = SampleUtil_GetFirstDocumentItem(DescDoc, "URLBase");
++
++    if( baseURL )
++        base = baseURL;
++    else
++        base = location;
++
++
++    serviceList = SampleUtil_GetFirstServiceList(DescDoc);
++    length = ixmlNodeList_length(serviceList);
++    for (i = 0; i < length; i++)
++	{
++        service = (IXML_Element *)ixmlNodeList_item(serviceList, i);
++        tempServiceType = SampleUtil_GetFirstElementItem((IXML_Element *)service, "serviceType");
++
++        if (strcmp(tempServiceType, serviceType) == 0) 
++		{
++			*serviceId = SampleUtil_GetFirstElementItem(service, "serviceId");
++			relSCPDURL = SampleUtil_GetFirstElementItem(service, "SCPDURL");
++			relcontrolURL = SampleUtil_GetFirstElementItem(service, "controlURL");
++			releventURL = SampleUtil_GetFirstElementItem(service, "eventSubURL");
++			
++			*SCPDURL = malloc(strlen(base) + strlen(relSCPDURL) + 1);
++			if(*SCPDURL)
++				ret = UpnpResolveURL(base, relSCPDURL, *SCPDURL);
++
++			*controlURL = malloc(strlen(base) + strlen(relcontrolURL) + 1);
++			if(*controlURL)
++				ret = UpnpResolveURL(base, relcontrolURL, *controlURL);
++
++			*eventURL = malloc(strlen(base) + strlen(releventURL) + 1);
++			if(*eventURL)
++				ret = UpnpResolveURL(base, releventURL, *eventURL);
++
++			if(relSCPDURL)
++				free(relSCPDURL);
++			if(relcontrolURL)
++				free(relcontrolURL);
++			if(releventURL)
++				free(releventURL);
++            relSCPDURL = relcontrolURL = releventURL = NULL;
++
++            found = 1;
++            break;
++        }
++
++        if (tempServiceType)
++            free(tempServiceType);
++        tempServiceType = NULL;
++    }
++
++    if( tempServiceType )
++        free( tempServiceType );
++    if( serviceList )
++        ixmlNodeList_free( serviceList );
++    if( baseURL )
++        free( baseURL );
++
++    return ( found );
++}
++
++
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/sample_util.h
+===================================================================
+--- /dev/null
++++ b/wsc/sample_util.h
+@@ -0,0 +1,146 @@
++///////////////////////////////////////////////////////////////////////////
++//
++// Copyright (c) 2000-2003 Intel Corporation 
++// All rights reserved. 
++//
++// Redistribution and use in source and binary forms, with or without 
++// modification, are permitted provided that the following conditions are met: 
++//
++// * Redistributions of source code must retain the above copyright notice, 
++// this list of conditions and the following disclaimer. 
++// * Redistributions in binary form must reproduce the above copyright notice, 
++// this list of conditions and the following disclaimer in the documentation 
++// and/or other materials provided with the distribution. 
++// * Neither name of Intel Corporation nor the names of its contributors 
++// may be used to endorse or promote products derived from this software 
++// without specific prior written permission.
++// 
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 
++// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
++// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
++// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
++// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
++// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++//
++///////////////////////////////////////////////////////////////////////////
++
++#ifndef SAMPLE_UTIL_H
++#define SAMPLE_UTIL_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdlib.h>
++#include <string.h>
++#include "upnptools.h"
++#include "ixml.h"
++
++typedef enum {
++	STATE_UPDATE = 0,
++	DEVICE_ADDED =1,
++	DEVICE_REMOVED=2,
++	GET_VAR_COMPLETE=3
++} eventType;
++
++
++/********************************************************************************
++ * SampleUtil_GetElementValue
++ *
++ * Description: 
++ *       Given a DOM node such as <Channel>11</Channel>, this routine
++ *       extracts the value (e.g., 11) from the node and returns it as 
++ *       a string. The string must be freed by the caller using 
++ *       free.
++ *
++ * Parameters:
++ *   node -- The DOM node from which to extract the value
++ *
++ ********************************************************************************/
++char * SampleUtil_GetElementValue(IN IXML_Element *element);
++
++/********************************************************************************
++ * SampleUtil_GetFirstServiceList
++ *
++ * Description: 
++ *       Given a DOM node representing a UPnP Device Description Document,
++ *       this routine parses the document and finds the first service list
++ *       (i.e., the service list for the root device).  The service list
++ *       is returned as a DOM node list. The NodeList must be freed using
++ *       NodeList_free.
++ *
++ * Parameters:
++ *   node -- The DOM node from which to extract the service list
++ *
++ ********************************************************************************/
++
++IXML_NodeList *SampleUtil_GetFirstServiceList(IN IXML_Document * doc); 
++
++
++/********************************************************************************
++ * SampleUtil_GetFirstDocumentItem
++ *
++ * Description: 
++ *       Given a document node, this routine searches for the first element
++ *       named by the input string item, and returns its value as a string.
++ *       String must be freed by caller using free.
++ * Parameters:
++ *   doc -- The DOM document from which to extract the value
++ *   item -- The item to search for
++ *
++ ********************************************************************************/
++char * SampleUtil_GetFirstDocumentItem(IN IXML_Document *doc, IN const char *item); 
++
++
++/********************************************************************************
++ * SampleUtil_GetFirstElementItem
++ *
++ * Description: 
++ *       Given a DOM element, this routine searches for the first element
++ *       named by the input string item, and returns its value as a string.
++ *       The string must be freed using free.
++ * Parameters:
++ *   node -- The DOM element from which to extract the value
++ *   item -- The item to search for
++ *
++ ********************************************************************************/
++char * SampleUtil_GetFirstElementItem(IN IXML_Element *element, IN const char *item); 
++
++
++/********************************************************************************
++ * SampleUtil_FindAndParseService
++ *
++ * Description: 
++ *       This routine finds the first occurance of a service in a DOM representation
++ *       of a description document and parses it.  Note that this function currently
++ *       assumes that the eventURL and controlURL values in the service definitions
++ *       are full URLs.  Relative URLs are not handled here.
++ *
++ * Parameters:
++ *   DescDoc -- The DOM description document
++ *   location -- The location of the description document
++ *   serviceSearchType -- The type of service to search for
++ *   serviceId -- OUT -- The service ID
++ *   eventURL -- OUT -- The event URL for the service
++ *   controlURL -- OUT -- The control URL for the service
++ *
++ ********************************************************************************/
++int SampleUtil_FindAndParseService (
++	IN IXML_Document *DescDoc,
++	IN char* location,
++	IN char *serviceType,
++	OUT char **serviceId,
++	OUT char **SCDPURL,
++	OUT char **eventURL,
++	OUT char **controlURL);
++
++#ifdef __cplusplus
++};
++#endif
++
++#endif /* UPNPSDK_UTIL_H */
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_common.c
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_common.c
+@@ -0,0 +1,221 @@
++#include <stdio.h>
++#include <stdarg.h>
++#include <stdlib.h>
++#include "wsc_common.h"
++
++
++static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
++static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
++
++
++extern int wsc_debug_level;
++
++#ifdef RT_DEBUG
++void DBGPRINTF(int level, char *fmt, ...)
++{
++	va_list ap;
++
++	va_start(ap, fmt);
++	if (level <= wsc_debug_level)
++	{
++		vprintf(fmt, ap);
++	}
++	va_end(ap);
++}
++#endif
++
++#ifdef ENABLE_WSC_SERVICE
++char * 
++WSCGetValueFromNameValueList(char *pbuf,
++                          const char *Name, int *len)
++{
++	char prefix[32], postfix[32], *ptr, *endptr;
++
++	*len = 0;
++	sprintf(prefix, "<%s", Name);
++	sprintf(postfix, "</%s>", Name);
++	ptr = strstr(pbuf, prefix);
++	ptr = strstr(ptr, ">");
++	/* for ">" */
++	ptr += 1;
++	endptr = strstr(pbuf, postfix);
++	if (ptr && endptr)
++	{
++		*len = endptr - ptr;
++	}
++
++	return ptr;
++}
++
++void wsc_chardump(char *title, char *ptr, int len)
++{
++
++	int32 i;
++	char *tmp = ptr;
++
++	if (RT_DBG_PKT <= wsc_debug_level)
++	{
++		printf("\n===StartOfMsgCharDump:%s\n", title);
++		for(i = 0; i < len; i++)
++		{
++			printf("%c", tmp[i] & 0xff);
++		}
++		printf("\n===EndOfMsgCharDump!\n");
++	}
++
++	return;
++}
++
++void wsc_hexdump(char *title, char *ptr, int len)
++{
++
++	int32 i;
++	char *tmp = ptr;
++
++	if (RT_DBG_PKT <= wsc_debug_level)
++	{
++		printf("\n---StartOfMsgHexDump:%s\n", title);
++		for(i = 0; i < len; i++)
++		{
++			if(i%16==0 && i!=0)
++				printf("\n");
++			printf("%02x ", tmp[i] & 0xff);
++		}
++		printf("\n---EndOfMsgHexDump!\n");
++	}
++
++	return;
++}
++#endif /* ENABLE_WSC_SERVICE */
++
++/* encode 3 8-bit binary bytes as 4 '6-bit' characters */
++void ILibencodeblock( unsigned char in[3], unsigned char out[4], int len )
++{
++	out[0] = cb64[ in[0] >> 2 ];
++	out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
++	out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
++	out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
++}
++
++/*! \fn ILibBase64Encode(unsigned char* input, const int inputlen, unsigned char** output)
++	\brief Base64 encode a stream adding padding and line breaks as per spec.
++	\par
++	\b Note: The encoded stream must be freed
++	\param input The stream to encode
++	\param inputlen The length of \a input
++	\param output The encoded stream
++	\returns The length of the encoded stream
++*/
++int ILibBase64Encode(unsigned char* input, const int inputlen, unsigned char** output)
++{
++	unsigned char* out;
++	unsigned char* in;
++	
++	*output = (unsigned char*)malloc(((inputlen * 4) / 3) + 5);
++	out = *output;
++
++	if (out == NULL)
++	{
++		return 0;
++	}
++
++	in  = input;
++	
++	if (input == NULL || inputlen == 0)
++	{
++		if (out)
++			free(out);
++		*output = NULL;
++		return 0;
++	}
++	
++	while ((in+3) <= (input+inputlen))
++	{
++		ILibencodeblock(in, out, 3);
++		in += 3;
++		out += 4;
++	}
++	if ((input+inputlen)-in == 1)
++	{
++		ILibencodeblock(in, out, 1);
++		out += 4;
++	}
++	else
++	if ((input+inputlen)-in == 2)
++	{
++		ILibencodeblock(in, out, 2);
++		out += 4;
++	}
++	*out = 0;
++	
++	return (int)(out-*output);
++}
++
++/* Decode 4 '6-bit' characters into 3 8-bit binary bytes */
++void ILibdecodeblock( unsigned char in[4], unsigned char out[3] )
++{
++	out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4);
++	out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2);
++	out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]);
++}
++
++/*! \fn ILibBase64Decode(unsigned char* input, const int inputlen, unsigned char** output)
++	\brief Decode a base64 encoded stream discarding padding, line breaks and noise
++	\par
++	\b Note: The decoded stream must be freed
++	\param input The stream to decode
++	\param inputlen The length of \a input
++	\param output The decoded stream
++	\returns The length of the decoded stream
++*/
++int ILibBase64Decode(unsigned char* input, const int inputlen, unsigned char** output)
++{
++	unsigned char* inptr;
++	unsigned char* out;
++	unsigned char v;
++	unsigned char in[4];
++	int i, len;
++	
++	if (input == NULL || inputlen == 0)
++	{
++		*output = NULL;
++		return 0;
++	}
++	
++	*output = (unsigned char*)malloc(((inputlen * 3) / 4) + 4);
++	out = *output;
++	inptr = input;
++	
++	while( inptr <= (input+inputlen) )
++	{
++		for( len = 0, i = 0; i < 4 && inptr <= (input+inputlen); i++ )
++		{
++			v = 0;
++			while( inptr <= (input+inputlen) && v == 0 ) {
++				v = (unsigned char) *inptr;
++				inptr++;
++				v = (unsigned char) ((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]);
++				if( v ) {
++					v = (unsigned char) ((v == '$') ? 0 : v - 61);
++				}
++			}
++			if( inptr <= (input+inputlen) ) {
++				len++;
++				if( v ) {
++					in[ i ] = (unsigned char) (v - 1);
++				}
++			}
++			else {
++				in[i] = 0;
++			}
++		}
++		if( len )
++		{
++			ILibdecodeblock( in, out );
++			out += len-1;
++		}
++	}
++	*out = 0;
++	return (int)(out-*output);
++}
++
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_common.h
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_common.h
+@@ -0,0 +1,277 @@
++#ifndef __WSC_COMMON_H__
++#define __WSC_COMMON_H__
++
++#include <stdio.h>
++#include <errno.h>
++#include <string.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include "config.h"
++
++#define WSC_VERSION   "0.1.1"
++
++#ifdef ENABLE_WSC_SERVICE
++
++#define DEFAULT_WPS_PORT (8888)
++
++extern unsigned short WPS_PORT;
++extern int netlink_sock;
++#ifndef TRUE
++#define TRUE 1
++#endif
++
++#ifndef FALSE
++#define FALSE 0
++#endif
++typedef char * DOMString;   
++#define IXML_Document DOMString
++
++
++/** {\bf UpnpAddToAction} creates an action request packet based on its input 
++ *  parameters (status variable name and value pair). This API is specially 
++ *  suitable inside a loop to add any number input parameters into an existing
++ *  action. If no action document exists in the beginning then a 
++ *  {\bf Upnp_Document} variable initialized with {\tt NULL} should be passed 
++ *  as a parameter.
++ *
++ *  @return [int] An integer representing one of the following:
++ *    \begin{itemize}
++ *      \item {\tt UPNP_E_SUCCESS}: The operation completed successfully.
++ *      \item {\tt UPNP_E_INVALID_PARAM}: One or more of the parameters 
++ *                                        are invalid.
++ *      \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to 
++ *              complete this operation.
++ *    \end{itemize}
++ */
++
++int UpnpAddToAction(
++        IXML_Document * ActionDoc, 
++	                              /** A pointer to store the action 
++				          document node. */
++        const char * ActionName,   /** The action name. */
++        const char * ServType,     /** The service type.  */
++        const char * ArgName,      /** The status variable name. */
++        const char * ArgVal        /** The status variable value.  */
++        );
++
++
++/** {\bf UpnpAddToActionResponse} creates an action response
++ *  packet based on its output parameters (status variable name
++ *  and value pair). This API is especially suitable inside
++ *  a loop to add any number of input parameters into an existing action 
++ *  response. If no action document exists in the beginning, a 
++ *  {\bf Upnp_Document} variable initialized with {\tt NULL} should be passed 
++ *  as a parameter.
++ *
++ *  @return [int] An integer representing one of the following:
++ *    \begin{itemize}
++ *      \item {\tt UPNP_E_SUCCESS}: The operation completed successfully.
++ *      \item {\tt UPNP_E_INVALID_PARAM}: One or more of the parameters 
++ *                                        are invalid.
++ *      \item {\tt UPNP_E_OUTOF_MEMORY}: Insufficient resources exist to 
++ *              complete this operation.
++ *    \end{itemize}
++ */
++
++int UpnpAddToActionResponse(
++        IXML_Document * ActionResponse, 
++	                                   /** Pointer to a document to 
++					       store the action document 
++					       node. */
++        const char * ActionName,        /** The action name. */
++        const char * ServType,          /** The service type.  */
++        const char * ArgName,           /** The status variable name. */
++        const char * ArgVal             /** The status variable value.  */
++        );
++
++
++char * 
++WSCGetValueFromNameValueList(char *pbuffer,
++                          const char * Name, int *len);
++#endif /* ENABLE_WSC_SERVICE */
++
++typedef unsigned char 	uint8;
++typedef unsigned short	uint16;
++typedef unsigned int 	uint32;
++typedef signed char 	int8;
++typedef signed short	int16;
++typedef signed int		int32;
++
++#ifndef PACKED
++#define PACKED  __attribute__ ((packed))
++#endif
++
++#ifndef IFLA_IFNAME
++#define IFLA_IFNAME 3
++#endif
++#ifndef IFLA_WIRELESS
++#define IFLA_WIRELESS 11
++#endif
++
++#ifndef ASSERT
++#define ASSERT(expr)	\
++	do{\
++		if(!(expr)) \
++			printf("%s(%d): ASSERTION Error!\n", __FUNCTION__, __LINE__); \
++	}while(0);
++#endif
++
++#ifndef RT_DEBUG
++#define DBGPRINTF(args...) do{}while(0)
++#else
++//void DBGPRINTF(int level, char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
++void DBGPRINTF(int level, char *fmt, ...);
++#endif
++
++
++#ifndef IFNAMSIZ
++#define	IFNAMSIZ	16
++#endif
++
++#define MAC_ADDR_LEN 			6
++#define LENGTH_802_1_H			8
++
++
++#define BIT(x)	(1<<x)
++
++extern int WscUPnPOpMode;
++
++extern char WSC_IOCTL_IF[IFNAMSIZ];
++extern unsigned char HostMacAddr[MAC_ADDR_LEN];	// Used to save the MAC address of local host.
++
++#define HandleLock()
++#define HandleUnlock()
++
++#define DEFAULT_PID_FILE_PATH		"/var/run/wscd.pid"
++
++#define USE_XML_TEMPLATE
++#define DEFAULT_WEB_ROOT_DIR	"/etc/xml/"
++#define DEFAULT_DESC_FILE_NAME	"WFADeviceDesc.xml"
++
++
++#define WSC_SYS_ERROR	(-1)
++#define WSC_SYS_SUCCESS 0
++
++typedef enum{
++	WSC_UPNP_OPMODE_DISABLE = 0,
++	WSC_UPNP_OPMODE_DEV = 1,
++	WSC_UPNP_OPMODE_CP = 2,
++	WSC_UPNP_OPMODE_BOTH = 3
++}WSC_UPNP_OPMODE;
++
++
++typedef enum{
++	RT_DBG_OFF		= 0,
++	RT_DBG_ERROR	= 1,
++	RT_DBG_PKT		= 2,
++	RT_DBG_INFO	= 3,
++	RT_DBG_LOUD	= 4,
++	RT_DBG_ALL
++}WSC_DEBUG_LEVEL;
++
++// 802.1x authentication format
++#define IEEE8021X_FRAME_VERSION		1
++#define IEEE8021X_FRAME_TYPE_EAP	0
++typedef	struct	PACKED _IEEE8021X_FRAME{
++	uint8	Version;					// 1.0
++	uint8	Type;						// 0 = EAP Packet
++	uint16	Length;
++}IEEE8021X_FRAME, *PIEEE8021X_FRAME;
++
++
++// EAP frame format
++typedef enum{
++	EAP_FRAME_CODE_REQUEST = 0x1,
++	EAP_FRAME_CODE_RESPONSE = 0x2
++}EAP_FRAME_CODE;
++
++typedef enum{
++	EAP_FRAME_TYPE_IDENTITY = 0x1,
++	EAP_FRAME_TYPE_WSC = 0xfe,
++}EAP_FRAME_TYPE;
++
++// EAP frame format
++typedef	struct PACKED _EAP_FRAME{
++	uint8	Code;						// 1 = Request, 2 = Response
++	uint8	Id;
++	uint16	Length;
++	uint8	Type;						// 1 = Identity, 0xfe = reserved, used by WSC
++}EAP_FRAME, *PEAP_FRAME;
++
++
++// KernelSpace 2 UserSpace msg header
++#define WSC_K2UMSG_FLAG_SUCCESS		BIT(0)
++#define WSC_K2UMSG_FLAG_ERROR		BIT(1)
++#define WSC_K2UMSG_FLAG_
++
++#define RTMP_WSC_NLMSG_HDR_LEN		30		//signature(8) + envID(4) + ackID(4) + msgLen(4) + Flag(2) + segLen(2) + devAddr(6)
++typedef struct PACKED _RTMP_WSC_NLMSG_HDR{
++	uint8	signature[8];	/* Signature used to identify that this's a Mediatek specific NETLINK message. 
++								MUST be "RAWSCMSG" currently.
++							*/
++	uint32	envID;			// Unique event Identification assigned by sender.
++	uint32	ackID;			// Notify that this message is a repsone for the message whose event identifier is "ackID".
++	uint32	msgLen;			// Totally length for this message. This message may seperate in serveral packets.
++	uint16	flags;			
++	uint16	segLen;			/* The "segLen" means the actual data length in this one msg packet.
++								Because the NETLINK socket just support 256bytes for "IWCUSTOM" typed message, so we may 
++								need to do fragement for our msg. If one message was fragemented as serveral pieces, the 
++								user space receiver need to re-assemble it.
++							 */
++	uint8	devAddr[MAC_ADDR_LEN];		// MAC address of the net device which send this netlink msg.
++}RTMP_WSC_NLMSG_HDR;
++
++#define RTMP_WSC_MSG_HDR_LEN		12	//msgType(2) + msgSubType(2) + ipAddr(4) + len(4)
++typedef struct PACKED _RTMP_WSC_MSG_HDR{
++	uint16	msgType;
++	uint16	msgSubType;
++	uint32	ipAddr;
++	uint32	msgLen;		//Not include this header.
++}RTMP_WSC_MSG_HDR;
++
++/*
++  1. This data structure used for UPnP daeom send WSC_MSG to wireless driver in Kernel space.
++  2. This data structure must sync with Mediatek wireless driver( defined in "wsc.h").
++  3. The size of this structure is equal to the (802.11 header+802.1h header+802.1x header+EAP header).
++  4. The Addr1 must set as all zero for notify the kernel driver that this packet was sent by UPnP daemon.
++	  (Because it's imposssible that AP receive a wireless packet from station whose addr1=0)
++  5. Please don't use sizeof(struct _WscIoctlMsgHdr) unless you "PACK" this data structure in kernel and here.
++*/
++#define WSC_U2KMSG_HDR_LEN	41	// HeaderLen = LENGTH_802_11(24) + LENGTH_802_1_H(8) + IEEE8021X_FRAME_HDR(4) + EAP_FRAME_HDR(5)
++typedef	struct PACKED _WSC_U2KMSG_HDR{
++	uint32				envID;					//Event ID.
++	char				Addr1[MAC_ADDR_LEN];	//RA, should be the MAC address of the AP.
++	char				Addr2[MAC_ADDR_LEN];	//TA, should be the ipAddress of remote UPnP Device/CotrnolPoint.
++	char				Addr3[MAC_ADDR_LEN];	//DA, Not used now.
++	char				rsvWLHdr[2];			//Reserved space for remained 802.11 hdr content.
++	char				rsv1HHdr[LENGTH_802_1_H];//Reserved space for 802.1h header
++	IEEE8021X_FRAME 	IEEE8021XHdr;			//802.1X header
++	EAP_FRAME			EAPHdr;					//EAP frame header.
++}RTMP_WSC_U2KMSG_HDR;
++
++void wsc_chardump(char *title, char *ptr, int len);
++void wsc_hexdump(char *title, char *ptr, int len);
++
++extern int ioctl_sock;
++
++int ILibBase64Encode(unsigned char* input, const int inputlen, unsigned char** output);
++int ILibBase64Decode(unsigned char* input, const int inputlen, unsigned char** output);
++
++
++int wscK2UModuleInit(void);
++int wscU2KModuleInit(void);
++int wsc_set_oid(uint16 oid, char *data, int len);
++int wsc_get_oid(uint16 oid, char *data, int *len);
++
++#ifdef MASK_PARTIAL_MACADDR
++#define MAC2STR(a) (a)[0], (a)[3], (a)[4], (a)[5]
++#define MACSTR "%02x:**:**:%02x:%02x:%02x"
++#else
++/* Debug print format string for the MAC Address */
++#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
++/* Debug print argument for the MAC Address */
++#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
++#endif /* MASK_PARTIAL_MACADDR */
++
++#endif
++
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_ioctl.c
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_ioctl.c
+@@ -0,0 +1,142 @@
++/*
++	function handler for ioctl handler
++*/
++#include <unistd.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <sys/ioctl.h>
++#include <linux/types.h>
++#include <linux/if.h>
++#include <linux/wireless.h>
++#include <netinet/in.h>
++#include "wsc_common.h"
++#include "wsc_ioctl.h"
++
++
++/******************************************************************************
++ * wsc_set_oid
++ *
++ * Description: 
++ *       Send Wsc Message to kernel space by ioctl function call.
++ *
++ * Parameters:
++ *    uint16 oid - The ioctl flag type for the ioctl function call.
++ *    char *data - The data message want to send by ioctl
++ *    int len    - The length of data 
++ *  
++ * Return Value:
++ *    Indicate if the ioctl success or failure.
++ *    	-1 = failure.
++ *    	0  = If success. 
++ *****************************************************************************/
++int wsc_set_oid(uint16 oid, char *data, int len)
++{
++	char *buf;
++	struct iwreq iwr;
++	
++	if(ioctl_sock < 0)
++		return -1;
++		
++	if((buf = malloc(len)) == NULL)
++		return -1;
++	
++	memset(buf, 0,len);
++	memset(&iwr, 0, sizeof(iwr));
++	strncpy(iwr.ifr_name, WSC_IOCTL_IF, IFNAMSIZ);
++	iwr.u.data.flags = oid;
++	iwr.u.data.flags |= OID_GET_SET_TOGGLE;
++		
++	if (data)
++		memcpy(buf, data, len);
++
++	iwr.u.data.pointer = (caddr_t) buf;
++	iwr.u.data.length = len;
++
++	if (ioctl(ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
++		DBGPRINTF(RT_DBG_INFO, "%s: oid=0x%x len (%d) failed", __FUNCTION__, oid, len);
++		free(buf);
++		return -1;
++	}
++	free(buf);
++	return 0;
++}
++
++
++/******************************************************************************
++ * wsc_get_oid
++ *
++ * Description: 
++ *       Get Wsc Message from kernel space by ioctl function call.
++ *
++ * Parameters:
++ *    uint16 oid - The ioctl flag type for the ioctl function call.
++ *    char *data - The data pointer want to receive msg from ioctl.
++ *    int len    - The length of returned data.
++ *  
++ * Return Value:
++ *    Indicate if the ioctl success or failure.
++ *    	-1 = failure.
++ *    	0  = If success. 
++ *****************************************************************************/
++int wsc_get_oid(uint16 oid, char *data, int *len)
++{
++	struct iwreq iwr;
++
++	if(ioctl_sock < 0 || data == NULL)
++		return -1;
++	
++	memset(&iwr, 0, sizeof(iwr));
++	strncpy(iwr.ifr_name, WSC_IOCTL_IF, IFNAMSIZ);
++	iwr.u.data.flags = oid;
++
++	iwr.u.data.pointer = (caddr_t)data;
++	if (ioctl(ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
++		DBGPRINTF(RT_DBG_ERROR, "%s: oid=0x%x failed", __FUNCTION__, oid);
++		return -1;
++	}
++	*len = iwr.u.data.length;
++	
++	return 0;
++}
++
++
++/******************************************************************************
++ * wscU2KModuleInit
++ *
++ * Description: 
++ *       Initialize the U2K Message subsystem(i.e. it's ioctl sub-system now).
++ *
++ * Parameters:
++ *    void
++ *  
++ * Return Value:
++ *    The created ioctl socket id or -1 to indicate if the initialize success or failure.
++ *    	-1 		  		 = failure.
++ *    	ioctl socket id  = If success. 
++ *****************************************************************************/
++int wscU2KModuleInit(void)
++{
++	int s;
++	struct ifreq ifr;
++	
++	/* open socket to kernel */
++	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
++	{
++		DBGPRINTF(RT_DBG_ERROR, "ioctl socket create failed! errno=%d!\n", errno);
++		return -1;
++	}
++	/* do it */
++	strncpy(ifr.ifr_name, WSC_IOCTL_IF, IFNAMSIZ);
++
++	if (ioctl(s, SIOCGIFINDEX, &ifr) < 0)
++	{
++		DBGPRINTF(RT_DBG_ERROR, "ioctl to get the interface(%s) index failed! errno=%d!\n" , ifr.ifr_name, errno);
++		return -1;
++	}
++
++	return s;
++
++}
++
++
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_ioctl.h
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_ioctl.h
+@@ -0,0 +1,24 @@
++/*
++	header file for wsc_ioctl
++
++*/
++#ifndef __WSC_IOCTL_H__
++#define __WSC_IOCTL_H__
++
++
++#if WIRELESS_EXT <= 11
++#ifndef SIOCDEVPRIVATE
++#define SIOCDEVPRIVATE                              0x8BE0
++#endif
++#define SIOCIWFIRSTPRIV								SIOCDEVPRIVATE
++#endif
++
++#define RT_PRIV_IOCTL								(SIOCIWFIRSTPRIV + 0x01)
++
++#define OID_GET_SET_TOGGLE							0x8000
++
++#define RT_OID_WSC_UUID								0x0753
++#define RT_OID_WSC_SET_SELECTED_REGISTRAR			0x0754
++#define RT_OID_WSC_EAPMSG							0x0755
++
++#endif
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_main.c
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_main.c
+@@ -0,0 +1,468 @@
++/***************************************
++	wsc main function
++****************************************/
++#include <stdio.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/time.h>
++#include <sys/resource.h>
++#include <sys/wait.h>
++#include <unistd.h> 
++#include <sys/socket.h> 
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#include <net/if.h> 
++#include <sys/ioctl.h> 
++	   
++//#include "config.h"
++#include "upnpglobalvars.h"
++#include "wsc_common.h"
++#include "wsc_msg.h"
++#include "wsc_upnp.h"
++
++#include "upnpreplyparse.h"
++
++//int wsc_debug_level = RT_DBG_OFF;
++int wsc_debug_level = RT_DBG_INFO;
++
++int ioctl_sock = -1;
++unsigned char UUID[16]= {0};
++
++int enableDaemon = 0;
++	
++static int wscUPnPDevInit = FALSE;
++static int wscK2UMInit = FALSE;
++static int wscU2KMInit = FALSE;
++static int wscMsgQInit = FALSE;
++
++
++int WscUPnPOpMode;
++//peter : 0523
++//char HostDescURL[WSC_UPNP_DESC_URL_LEN] = {0};			// Used to save the DescriptionURL of local host.
++unsigned char HostMacAddr[MAC_ADDR_LEN]={0};			// Used to save the MAC address of local host.
++unsigned int HostIPAddr;								// Used to save the binded IP address of local host.
++char WSC_IOCTL_IF[IFNAMSIZ];
++unsigned short WPS_PORT;
++#if 1 //def MULTIPLE_CARD_SUPPORT
++#define FILE_PATH_LEN	256
++char pid_file_path[FILE_PATH_LEN];
++#endif // MULTIPLE_CARD_SUPPORT //
++
++void usage(void)
++{
++	printf("Usage: miniupnpd -G [-I infName] [-A ipaddress] [-n port] [-F descDoc] [-w webRootDir] -m UPnPOpMode -D [-l debugLevel] -h\n");
++	printf("\t-G:      Enable IGD Device service in miniupnpd\n");
++	printf("\t-I:	   Interface name this daemon will run wsc protocol(if not set, will use the default interface name - ra0)\n");
++	printf("\t\t\te.g.: ra0\n");
++	printf("\t-A:       IP address of the device (if not set, will auto assigned)\n");
++	printf("\t\t\t e.g.: 192.168.0.1\n");
++	printf("\t-n:       Port number for receiving UPnP messages (if not set, will auto assigned)\n");
++	printf("\t\t\t e.g.: 54312\n");
++	printf("\t-F:       Name of device description document (If not set, will used default value)\n");
++	printf("\t\t\t e.g.: WFADeviceDesc.xml\n");
++	printf("\t-w:       Filesystem path where descDoc and web files related to the device are stored\n");
++	printf("\t\t\t e.g.: /etc/xml/\n");
++	printf("\t-m:       UPnP system operation mode\n");
++	printf("\t\t\t 1: Enable UPnP Device service(Support Enrolle or Proxy functions)\n");
++	printf("\t\t\t 2: Enable UPnP Control Point service(Support Registratr function)\n");
++	printf("\t\t\t 3: Enable both UPnP device service and Control Point services.\n");
++	printf("\t-D:       Run program in daemon mode.\n");
++	printf("\t-l:       Debug level of WPS module.\n");
++	printf("\t\t\t 0: debug printing off\n");
++	printf("\t\t\t 1: DEBUG_ERROR\n");
++	printf("\t\t\t 2: DEBUG_PKT\n");
++	printf("\t\t\t 3: DEBUG_INFO\n");
++	
++	exit(1);
++
++}
++
++
++void sig_handler(int sigNum)
++{
++	sig_atomic_t status;
++
++	DBGPRINTF(RT_DBG_INFO, "Get a signal=%d!\n", sigNum);
++	switch(sigNum)
++	{
++		case SIGCHLD:
++			wait3(&status, WNOHANG, NULL);
++			break;
++		default:
++			break;
++	}
++
++}
++
++int wscSystemInit(void)
++{
++	int retVal;
++	
++	struct sigaction sig;
++	memset(&sig, 0, sizeof(struct sigaction));
++	sig.sa_handler= &sig_handler;
++	sigaction(SIGCHLD, &sig, NULL);
++	
++	retVal = wscMsgSubSystemInit();
++	
++	return retVal;
++}
++
++
++int wscSystemStop(void)
++{
++	/* Stop the K2U and U2K msg subsystem */
++	if (wscU2KMInit && ioctl_sock >= 0) 
++	{
++		// Close the ioctl socket.
++		close(ioctl_sock);
++		ioctl_sock = -1;
++	}
++	
++	if (wscK2UMInit)
++	{
++
++	}
++
++	if(wscUPnPDevInit)
++	{
++		WscUPnPDevStop();
++	}
++/*
++	if(wscUPnPCPInit)
++		WscUPnPCPStop();
++*/
++#if 0	
++	if(wscUPnPSDKInit)
++		UpnpFinish();
++#endif
++
++	if( wscMsgQInit)
++	{
++		wscMsgSubSystemStop();
++	}
++	return 1;
++}
++
++/******************************************************************************
++ * WscDeviceCommandLoop
++ *
++ * Description: 
++ *       Function that receives commands from the user at the command prompt
++ *       during the lifetime of the device, and calls the appropriate
++ *       functions for those commands. Only one command, exit, is currently
++ *       defined.
++ *
++ * Parameters:
++ *    None
++ *
++ *****************************************************************************/
++void *
++WscDeviceCommandLoop( void *args )
++{
++    int stoploop = 0;
++    char cmdline[100];
++    char cmd[100];
++
++    while( !stoploop ) {
++        sprintf( cmdline, " " );
++        sprintf( cmd, " " );
++
++		printf( "\n>> " );
++
++        // Get a command line
++        fgets( cmdline, 100, stdin );
++
++        sscanf( cmdline, "%s", cmd );
++
++        if( strcasecmp( cmd, "exit" ) == 0 ) {
++			printf( "Shutting down...\n" );
++			wscSystemStop();
++        } else {
++			printf("\n   Unknown command: %s\n\n", cmd);
++			printf("   Valid Commands:\n" );
++			printf("     Exit\n\n" );
++        }
++
++    }
++
++    return NULL;
++}
++
++
++
++/******************************************************************************
++ * main of WPS module
++ *
++ * Description: 
++ *       Main entry point for WSC device application.
++ *       Initializes and registers with the sdk.
++ *       Initializes the state stables of the service.
++ *       Starts the command loop.
++ *
++ * Parameters:
++ *    int argc  - count of arguments
++ *    char ** argv -arguments. The application 
++ *                  accepts the following optional arguments:
++ *
++ *          -ip 	ipAddress
++ *          -port 	port
++ *		    -desc 	descDoc
++ *	        -webdir webRootDir"
++ *		    -help 
++ *          -i      ioctl binding interface.
++ *
++ *****************************************************************************/
++int WPSInit(int argc, char **argv)
++{
++	unsigned int portTemp = 0;
++	char *ipAddr = NULL, *descDoc = NULL, *webRootDir = NULL, *infName = NULL;
++//	unsigned short port = 0;
++	FILE *fp;
++	int retVal;
++	int opt;
++
++	if (argc < 2)
++		usage();
++
++	printf("\n@@@@@@@@@ WPSInit @@@@@@@@@\n");
++
++	/* default op mode is device */
++	WscUPnPOpMode = WSC_UPNP_OPMODE_DEV;
++	/* default op mode is device */
++	memset(&WSC_IOCTL_IF[0], 0, IFNAMSIZ);
++	strcpy(&WSC_IOCTL_IF[0], "ra0");
++	
++	/* first, parsing the input options */
++	while((opt = getopt(argc, argv, "A:I:n:F:w:m:l:DGdh"))!= -1)
++	{
++		switch (opt)
++		{
++			case 'A':
++				ipAddr = optarg;
++				break;
++			case 'n':
++				sscanf(optarg, "%u", &portTemp);
++				break;
++			case 'F':
++				descDoc = optarg;
++				break;
++			case 'I':
++				infName = optarg;
++				memset(&WSC_IOCTL_IF[0], 0, IFNAMSIZ);
++				if (strlen(infName))
++					strncpy(&WSC_IOCTL_IF[0], infName, IFNAMSIZ);
++				else
++					strcpy(&WSC_IOCTL_IF[0], "ra0");
++				break;
++			case 'w':
++				webRootDir = optarg;
++				break;
++			case 'm':
++				WscUPnPOpMode = strtol(optarg, NULL, 10);
++				if (WscUPnPOpMode < WSC_UPNP_OPMODE_DEV || WscUPnPOpMode > WSC_UPNP_OPMODE_BOTH)
++					usage();
++				break;
++			case 'D':
++				enableDaemon = 1;
++				break;
++			case 'l':
++				wsc_debug_level = strtol(optarg, NULL, 10);
++				break;
++			case 'G':
++				/* dummy option - In fact : be handled in miniupnpd */
++				break;
++			case 'd':
++				/* dummy option - In fact : be handled in miniupnpd */
++				break;	
++			case 'h':
++				usage();
++				break;
++        }
++    }
++
++	if ((WscUPnPOpMode < WSC_UPNP_OPMODE_DEV) || (WscUPnPOpMode > WSC_UPNP_OPMODE_BOTH))
++	{
++		fprintf(stderr, "Wrong UPnP Operation Mode: %d\n", WscUPnPOpMode);
++		usage();
++	}
++	if ((wsc_debug_level > RT_DBG_ALL)  || (wsc_debug_level < RT_DBG_OFF))
++	{
++		fprintf(stderr, "Wrong Debug Level: %d\n", wsc_debug_level);
++		usage();
++	}				
++
++	if (enableDaemon)
++	{
++		pid_t childPid;
++
++		childPid = fork();
++		if(childPid < 0)
++		{
++			fprintf(stderr, "Run in daemon mode failed --ErrMsg=%s!\n", strerror(errno));
++			exit(0);
++		} 
++		else if (childPid > 0)
++			exit(0);
++		else 
++		{
++			/* normal programming style to make a program work as a daemon */
++			close(0); /* close the stdin fd */
++			close(1); /* close the stdout fd */
++		}	
++	}
++
++	/* Write the pid file */
++#if 1 // def MULTIPLE_CARD_SUPPORT
++	memset(&pid_file_path[0], 0, FILE_PATH_LEN);
++	sprintf(pid_file_path, "%s.%s", DEFAULT_PID_FILE_PATH, WSC_IOCTL_IF);
++	DBGPRINTF(RT_DBG_INFO, "The pid file is: %s!\n", pid_file_path);
++	if ((fp = fopen(pid_file_path, "w")) != NULL)
++#else
++	if((fp = fopen(DEFAULT_PID_FILE_PATH, "w"))!= NULL)
++#endif // MULTIPLE_CARD_SUPPORT //
++	{
++		fprintf(fp, "%d", getpid());
++		fclose(fp);
++	}
++	
++	/* System paramters initialization */
++	if (wscSystemInit() == WSC_SYS_ERROR)
++	{
++		fprintf(stderr, "wsc MsgQ System Initialization failed!\n");
++		goto STOP;
++	}
++	wscMsgQInit = TRUE;
++	
++	/* Initialize the netlink interface from kernel space */
++	if(wscK2UModuleInit() != WSC_SYS_SUCCESS)
++	{	
++		fprintf(stderr, "creat netlink socket failed!\n");
++		goto STOP;
++	}
++	else 
++	{
++		DBGPRINTF(RT_DBG_INFO, "Create netlink socket success!\n");
++		wscK2UMInit = TRUE;
++	}
++	
++	/* Initialize the ioctl interface for data path to kernel space */
++	ioctl_sock = wscU2KModuleInit();
++	if(ioctl_sock == -1)
++	{
++		fprintf(stderr, "creat ioctl socket failed!err=%d!\n", errno);
++		goto STOP;
++	}
++	else
++	{
++		DBGPRINTF(RT_DBG_INFO, "Create ioctl socket(%d) success!\n", ioctl_sock);
++		wscU2KMInit = TRUE;
++	}
++
++	/* Initialize the upnp related data structure and start upnp service */
++	if(WscUPnPOpMode)
++	{
++		struct ifreq  ifr;
++#if 0			
++		// Initializing UPnP SDK
++		if ((retVal = UpnpInit(ipAddr, port)) != 0)
++		{
++			DBGPRINTF(RT_DBG_ERROR, "Error with UpnpInit -- %d\n", retVal);
++			UpnpFinish();
++			goto STOP;
++		}
++		wscUPnPSDKInit = TRUE;
++#endif
++		// Get the IP/Port the UPnP services want to bind.
++
++		if (ipAddr == NULL)
++		{
++//			ipAddr = UpnpGetServerIpAddress();
++//			ipAddr = "192.168.15.43";
++#if 1
++#if 1 //Porting for miniupnpd 1.6
++			struct lan_addr_s * tmp_addr;
++			tmp_addr = lan_addrs.lh_first;
++			if (tmp_addr!=NULL)
++			{
++				ipAddr =tmp_addr->str;
++			}
++#else
++			ipAddr = lan_addr[0].str;
++#endif
++#else
++			if ((lan_addr[n_lan_addr-1].str != NULL) && (GETFLAG(ENABLEWPSMASK)))
++				ipAddr = lan_addr[n_lan_addr-1].str;
++#endif
++		}
++		ASSERT(ipAddr != NULL);
++		inet_aton(ipAddr, (struct in_addr *)&HostIPAddr);
++		
++		if (portTemp != 0)
++		{
++		    WPS_PORT = (unsigned short)portTemp;
++		}
++		else
++		{
++			/* set default port number for WPS service */
++			WPS_PORT = (unsigned short)DEFAULT_WPS_PORT;
++		}
++
++		ASSERT(WPS_PORT > 0);
++
++		// Get the Mac Address of wireless interface
++		memset(&ifr, 0, sizeof(struct ifreq));
++		strcpy(ifr.ifr_name, WSC_IOCTL_IF); 
++		if (ioctl(ioctl_sock, SIOCGIFHWADDR, &ifr) > 0) 
++        { 
++			perror("ioctl to get Mac Address");
++			goto STOP;
++		}
++		memcpy(HostMacAddr, ifr.ifr_hwaddr.sa_data, MAC_ADDR_LEN);
++
++		DBGPRINTF(RT_DBG_INFO, "\t HW-Addr: "MACSTR"!\n", MAC2STR(HostMacAddr));
++
++		// Start UPnP Device Service.
++		if (WscUPnPOpMode & WSC_UPNP_OPMODE_DEV)
++		{	  
++		    retVal = WscUPnPDevStart(ipAddr, WPS_PORT, descDoc, webRootDir);
++			if (retVal != WSC_SYS_SUCCESS)
++				goto STOP;
++			wscUPnPDevInit = TRUE;
++		}
++#if 0 /* apply for control point only */
++		// Start UPnP Control Point Service.
++		if(WscUPnPOpMode & WSC_UPNP_OPMODE_CP)
++		{
++			retVal = WscUPnPCPStart(ipAddr, port);
++			if (retVal != WSC_SYS_SUCCESS)
++				goto STOP;
++			wscUPnPCPInit = TRUE;
++		}
++#endif
++	}
++
++#if 0 /* handled in main() of miniupnpd */
++    /* Catch Ctrl-C and properly shutdown */
++    sigemptyset(&sigs_to_catch);
++    sigaddset(&sigs_to_catch, SIGINT);
++    sigwait(&sigs_to_catch, &sig);
++
++    DBGPRINTF(RT_DBG_INFO, "Shutting down on signal %d...\n", sig);
++#endif
++
++	return 1;	
++
++    DBGPRINTF(RT_DBG_INFO, "wscSystemStop()...\n");
++    wscSystemStop();
++STOP:
++		/* Trigger other thread to stop the procedures. */
++#if 1 //def MULTIPLE_CARD_SUPPORT
++	unlink(pid_file_path);
++#else
++	unlink(DEFAULT_PID_FILE_PATH);
++#endif // MULTIPLE_CARD_SUPPORT //
++	return 0;
++//    exit(0);
++}
++
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_msg.c
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_msg.c
+@@ -0,0 +1,569 @@
++/*
++	wsc_msg.c used to handle wsc msg system.
++*/
++#include <pthread.h>
++#include "wsc_msg.h"
++#include "wsc_upnp.h"
++#include "wsc_common.h"
++
++
++/* Used for Passive Queue system */
++#define WSCPASSIVEMSGQ_MAX_ENTRY	32
++
++static uint32 gPassiveMsgID=0;
++static WscU2KMsgQ gPassiveMsgQ[WSCPASSIVEMSGQ_MAX_ENTRY];
++
++/* Used for Active Queue system */
++#define WSCACTIVEMSGQ_MAX_ENTRY	32
++
++static pthread_mutex_t gActiveMsgQLock;
++static WscU2KMsgQ gActiveMsgQ[WSCACTIVEMSGQ_MAX_ENTRY];
++
++/* Used for Active Message Handler callback fucntion list */
++#define WSCACTIVEMSGHANDLE_LSIT_MAX_ENTRY    3
++
++static WscMsgHandle gActiveMsgHandleList[WSCACTIVEMSGHANDLE_LSIT_MAX_ENTRY]={
++	{WSC_OPCODE_UPNP_DATA, WscEventDataMsgRecv},
++	{WSC_OPCODE_UPNP_MGMT, WscEventMgmtMsgRecv},
++	{WSC_OPCODE_UPNP_CTRL, WscEventCtrlMsgRecv},
++};
++
++	
++int wscEnvelopeFree(IN WscEnvelope *envelope)
++{
++	if(envelope)
++	{
++		if(envelope->pMsgPtr)
++			free(envelope->pMsgPtr);
++		free(envelope);
++	}
++
++	return 0;
++}
++
++int wscEnvelopeRemove(
++	IN WscEnvelope *envelope,
++	OUT int qIdx)
++{
++	if(!envelope)
++		return WSC_SYS_ERROR;
++	
++	wscEnvelopeFree(envelope);
++	if((qIdx >= 0) && (qIdx < WSCPASSIVEMSGQ_MAX_ENTRY))
++	{
++		gPassiveMsgQ[qIdx].pEnvelope = NULL;
++		gPassiveMsgQ[qIdx].used = 0;
++	}
++	
++	return WSC_SYS_SUCCESS;;
++}
++
++
++/******************************************************************************
++ * wscEnvelopeCreate
++ *
++ * Description: 
++ *       Allocate the memory space for a new envelope and init it.
++ *
++ * Parameters:
++ *    void
++ 
++ * Return Value:
++ *    The pointer of cretaed envelope.
++ *    	NULL = failure.
++ *    	others = True. 
++ *****************************************************************************/
++WscEnvelope *wscEnvelopeCreate(void)
++{
++	WscEnvelope *envelope = NULL;
++	
++	if((envelope = malloc(sizeof(WscEnvelope))) == NULL)
++	{
++		DBGPRINTF(RT_DBG_ERROR, "Allocate memory to create a new envelope failed!\n");
++		return NULL;
++	}
++	
++	memset(envelope, 0 ,sizeof(WscEnvelope));
++
++	envelope->h = NULL;
++	envelope->DevCallBack = NULL;
++	envelope->callBack = NULL;
++	envelope->timerCount = DEF_WSC_ENVELOPE_TIME_OUT;
++	return envelope;
++}
++
++
++/******************************************************************************
++ * wscEnvelopeCreate
++ *
++ * Description: 
++ *       Insert a envelope to the message Queue(gPassiveMsgQ) and assign a 
++ *       eventID to this envelope.
++ *
++ * Parameters:
++ *    WscEnvelope *envelope - The envelope want to insert.
++ *    int *pQIdx            - The msg Q index of the envelope inserted in.
++ *  
++ * Return Value:
++ *    Indicate if the envelope insetion success or failure.
++ *    	WSC_SYS_SUCCESS = failure.
++ *    	WSC_SYS_ERROR   = True. 
++ *****************************************************************************/
++int wscMsgQInsert(
++	IN WscEnvelope *envelope,
++	OUT int *pQIdx)
++{
++	int idx;
++
++	if(!envelope)
++		return WSC_SYS_ERROR;
++	
++	for(idx = 0; idx < WSCPASSIVEMSGQ_MAX_ENTRY; idx++)
++	{
++		if(gPassiveMsgQ[idx].used == 0)
++		{
++			envelope->envID = ((++gPassiveMsgID) == 0 ? (++gPassiveMsgID) : gPassiveMsgID); //ID 0 is reserved for msg directly send by kernel.
++			gPassiveMsgQ[idx].pEnvelope = envelope;
++			gPassiveMsgQ[idx].used = 1;
++			
++			break;
++		}
++	}
++	
++	if(idx < WSCPASSIVEMSGQ_MAX_ENTRY)
++	{
++		*pQIdx = idx;
++		DBGPRINTF(RT_DBG_INFO, "Insert envelope to msgQ success, msgQIdx = %d!envID=0x%x!\n", idx, envelope->envID);
++		return WSC_SYS_SUCCESS;
++	}
++	
++	return WSC_SYS_ERROR;
++}
++
++
++/******************************************************************************
++ * wscMsgQRemove
++ *
++ * Description: 
++ *    	Remove the envelope in the message Queue. 
++ * Note: 
++ *  	This function just remove the envelope from the Q but not free it. You 
++ * 		should free it yourself. 
++ *
++ * Parameters:
++ *    int qType - The Message Queue type want to handle.
++ *    int qIdx  - The msg Q index want to be removed.
++ *  
++ * Return Value:
++ *    Indicate if the envelope delete success or failure.
++ *    	WSC_SYS_SUCCESS = failure.
++ *    	WSC_SYS_ERROR   = True. 
++ *****************************************************************************/
++int wscMsgQRemove(
++	IN int qType, 
++	IN int qIdx)
++{
++	// The ActiveQ and PassiveQ have the same maximum capacity of the envelope.
++	if (qIdx < 0 || qIdx >= WSCPASSIVEMSGQ_MAX_ENTRY)
++		return WSC_SYS_ERROR;
++
++	if(qType == Q_TYPE_ACTIVE)
++	{	// Handle the Active Q.
++		if(pthread_mutex_lock(&gActiveMsgQLock) == 0)
++		{
++			wscEnvelopeFree(gActiveMsgQ[qIdx].pEnvelope);
++		gActiveMsgQ[qIdx].pEnvelope = NULL;
++		gActiveMsgQ[qIdx].used = 0;
++			pthread_mutex_unlock(&gActiveMsgQLock);
++	} 
++	} 
++	else if(qType ==Q_TYPE_PASSIVE)
++	{	// Handle the Passive Q.
++		wscEnvelopeFree(gPassiveMsgQ[qIdx].pEnvelope);
++		gPassiveMsgQ[qIdx].pEnvelope = NULL;
++		gPassiveMsgQ[qIdx].used = 0;
++	}
++	else
++	{
++		DBGPRINTF(RT_DBG_ERROR, "%s():Wrong MsgQ Type -- type=%d!\n", __FUNCTION__, qType);
++	}
++	
++	return WSC_SYS_ERROR;
++	
++}
++
++int registerActiveMsgTypeHandle(
++	IN int MsgType, 
++	IN msgHandleCallback handle)
++{
++
++	return -1;
++}
++
++int wscActiveMsgHandler(IN WscEnvelope* pEnvelope)
++{
++	int i;
++	RTMP_WSC_MSG_HDR *pHdr = NULL;
++	
++	pHdr = (RTMP_WSC_MSG_HDR *)pEnvelope->pMsgPtr;
++
++	DBGPRINTF(RT_DBG_INFO, "%s(): the ActiveMsgType=%d, SubType=%d!\n", __FUNCTION__, pHdr->msgType, pHdr->msgSubType);
++	for(i=0; i< WSCACTIVEMSGHANDLE_LSIT_MAX_ENTRY; i++)
++	{
++		if((gActiveMsgHandleList[i].msgType == pHdr->msgType) && (gActiveMsgHandleList[i].handle != NULL))
++		{
++			DBGPRINTF(RT_DBG_INFO, "find ActiveMsgHandler!\n");
++			return (gActiveMsgHandleList[i].handle)((char *)pEnvelope->pMsgPtr, pEnvelope->msgLen);
++		}
++	}
++
++	return 0;
++}
++
++
++int wscMsgStateUpdate(char *pBuf, int len)
++{
++
++	/*
++		TODO:  If we want to maintain our UPnP engine status. We need to add the code here. 
++	*/
++#if 0
++	RTMP_WSC_MSG_HDR *pHdr;
++	
++	pHdr = (RTMP_WSC_MSG_HDR *)pBuf;
++
++	if(pHdr == NULL)
++		return 0;
++
++	if(pHdr->msgType != WSC_OPCODE_UPNP_DATA)
++		return 0;
++#endif
++	return 0;
++	
++}
++
++
++/*
++	NETLINK tunnel msg format send to WSCUPnP handler in user space:
++	1. Signature of following string(Not include the quote, 8 bytes)
++			"RAWSCMSG"
++	2. eID: eventID (4 bytes)
++			the ID of this message(4 bytes)
++	3. aID: ackID (4 bytes)
++			means that which event ID this mesage was response to.
++	4. TL:  Message Total Length (4 bytes) 
++			Total length of this message.
++	5. F:   Flag (2 bytes)
++			used to notify some specific character of this msg segment.
++				Bit 1: fragment
++					set as 1 if netlink layer have more segment of this Msg need to send.
++				Bit 2~15: reserve, should set as 0 now.
++	5. SL:  Segment Length(2 bytes)
++			msg actual length in this segment, The SL may not equal the "TL" field if "F" ==1
++	6. devMac: device mac address(6 bytes)
++			Indicate the netdevice which this msg belong. For the wscd in user space will 
++			depends this address dispatch the msg to correct UPnP Device instance to handle it.
++	7. "WSC_MSG" info:
++
++                 8                 4       4       4      2    2        6      variable length(MAXIMA=232)
++	+------------+----+----+----+--+--+------+------------------------+
++	|  Signature       |eID  |aID  | TL   | F | SL|devMac| WSC_MSG                          |
++
++*/
++int WscRTMPMsgHandler(char *pBuf, int len)
++{
++	char *pos, *pBufPtr;
++	int dataLen = 0, qIdx, workQIdx = -1;
++	RTMP_WSC_NLMSG_HDR *pSegHdr;
++	WscEnvelope *pEnvPtr = NULL;
++	int qType;
++	
++	pSegHdr = (RTMP_WSC_NLMSG_HDR *)pBuf;
++	
++	
++#if 1 // def MULTIPLE_CARD_SUPPORT
++	if (memcmp(&pSegHdr->devAddr[0],  &HostMacAddr[0], MAC_ADDR_LEN) != 0)
++	{
++		DBGPRINTF(RT_DBG_INFO, "This msg are not the device we served("MACSTR")!!\n", 
++								getpid(), MAC2STR(HostMacAddr));
++		return 0;
++	}
++#endif // MULTIPLE_CARD_SUPPORT //
++
++	DBGPRINTF(RT_DBG_INFO, "pSegHdr->envID=0x%08x, ackID=0x%08x, devAddr="MACSTR"\n", 
++							pSegHdr->envID, pSegHdr->ackID, MAC2STR(pSegHdr->devAddr));
++	
++	if(pSegHdr->ackID == 0)
++	{
++		int emptyIdx = -1;
++		
++		/* The message should insert into the ActiveMsgQ */
++		pthread_mutex_lock(&gActiveMsgQLock);
++		for(qIdx = 0; qIdx < WSCACTIVEMSGQ_MAX_ENTRY; qIdx++)
++		{	// Search for mailBox to check if we have a matched eventID.
++			if((gActiveMsgQ[qIdx].used == 1) && ((pEnvPtr = gActiveMsgQ[qIdx].pEnvelope) != NULL))
++			{
++				if(pEnvPtr->envID == pSegHdr->envID)
++				{
++					workQIdx = qIdx;
++					pEnvPtr->timerCount = DEF_WSC_ENVELOPE_TIME_OUT;
++					break;
++				}
++			}
++			else if(emptyIdx < 0 && gActiveMsgQ[qIdx].used == 0){
++				emptyIdx = qIdx; //record the first un-used qIdx.
++			}
++		}
++		
++		if((qIdx == WSCACTIVEMSGQ_MAX_ENTRY) && (emptyIdx >= 0))
++		{
++			pEnvPtr = wscEnvelopeCreate();
++			if(pEnvPtr != NULL)
++			{	
++				wscEnvelopeFree(gActiveMsgQ[emptyIdx].pEnvelope);
++				pEnvPtr->envID = pSegHdr->envID;
++				gActiveMsgQ[emptyIdx].used = 1;
++				gActiveMsgQ[emptyIdx].pEnvelope = pEnvPtr;
++				workQIdx = emptyIdx;
++			}
++		} 
++		pthread_mutex_unlock(&gActiveMsgQLock);
++		
++		if(pEnvPtr == NULL)
++			return -1;
++		
++		qType = Q_TYPE_ACTIVE;
++
++		//For debug
++		if (pEnvPtr->pMsgPtr == NULL) {
++			DBGPRINTF(RT_DBG_INFO, "Receive a new wireless event(WscActiveMsg):\n"
++									"\tMsgHdr->msgLen=%d\n"
++									"\tMsgHdr->envID=0x%08x\n"
++									"\tMsgHdr->ackID=0x%08x\n"
++									"\tMsgHdr->flags=0x%04x\n"
++									"\tMsgHdr->segLen=%d\n", 
++									pSegHdr->msgLen, pSegHdr->envID, pSegHdr->ackID, 
++									pSegHdr->flags, pSegHdr->segLen);
++		}
++	}
++	else
++	{
++		/* The message should insert into the PassiveMsgQ */
++		for(qIdx = 0; qIdx < WSCPASSIVEMSGQ_MAX_ENTRY; qIdx++)
++		{	// Search for mailBox to check if we have a matched ackID.
++			if(gPassiveMsgQ[qIdx].used == 1 && ((pEnvPtr = gPassiveMsgQ[qIdx].pEnvelope) != NULL))
++			{
++				if(pEnvPtr->envID == pSegHdr->ackID)
++				{
++					pEnvPtr->timerCount = DEF_WSC_ENVELOPE_TIME_OUT;
++					break;
++				}
++			}
++		}
++		
++		if(qIdx == WSCPASSIVEMSGQ_MAX_ENTRY)	//Queue is full, drop it directly?
++			return -1;
++
++		qType = Q_TYPE_PASSIVE;
++
++		//For debug
++		if (pEnvPtr->pMsgPtr == NULL) {
++			DBGPRINTF(RT_DBG_INFO, "Receive a new wireless event(WscPassiveMsg):\n"
++									"\tMsgHdr->msgLen=%d\n"
++									"\tMsgHdr->envID=0x%x\n"
++									"\tMsgHdr->ackID=0x%x\n"
++									"\tMsgHdr->flags=0x%x\n"
++									"\tMsgHdr->segLen=%d\n", 
++									pSegHdr->msgLen,  pSegHdr->envID, pSegHdr->ackID, 
++									pSegHdr->flags, pSegHdr->segLen);
++		}
++	}
++
++	//Now handle the wsc_msg payload.
++	pos = ((char *)pSegHdr + sizeof(RTMP_WSC_NLMSG_HDR));
++	dataLen = pSegHdr->segLen;
++	if (pos)
++	{
++		/* TODO: Need to handle it to Upnp Module */
++		//wsc_hexdump("WSCENROLMSG", pos, dataLen);
++		if (pEnvPtr->pMsgPtr == NULL)
++		{	
++			if ((pEnvPtr->pMsgPtr = malloc(pSegHdr->msgLen)) != NULL)
++			{
++				pEnvPtr->msgLen = pSegHdr->msgLen;
++				pEnvPtr->actLen = 0;
++			} else {
++				DBGPRINTF(RT_DBG_ERROR, "%s():allocate memory for pEnvPtr->pMsgPtr failed!\n", __FUNCTION__);
++				pEnvPtr->flag = WSC_ENVELOPE_MEM_FAILED;
++			}
++		} 
++
++		if((pEnvPtr->flag & WSC_ENVELOPE_MEM_FAILED) != WSC_ENVELOPE_MEM_FAILED)
++		{
++			pBufPtr = &((char *)pEnvPtr->pMsgPtr)[pEnvPtr->actLen];
++			memcpy(pBufPtr, pos, dataLen);
++			pEnvPtr->actLen += dataLen;
++
++			if (pSegHdr->flags == 0)
++				pEnvPtr->flag = WSC_ENVELOPE_SUCCESS;
++		}
++	}
++
++	// Trigger the event handler to process the received msgs.
++	if(pEnvPtr->flag != WSC_ENVELOPE_NONE)
++	{
++		// Check and update the WSC status depends on the messages.
++		if(pEnvPtr->flag == WSC_ENVELOPE_SUCCESS)
++			wscMsgStateUpdate(pEnvPtr->pMsgPtr, pEnvPtr->msgLen);
++
++		// Deliver the message to corresponding handler.
++		if(qType == Q_TYPE_PASSIVE)
++		{
++			gPassiveMsgQ[qIdx].used = 0;
++			gPassiveMsgQ[qIdx].pEnvelope = NULL;
++			
++			//wsc_hexdump("NLMsg", pEnvPtr->pMsgPtr, pEnvPtr->msgLen);
++			if(pEnvPtr->DevCallBack != NULL) 
++			{
++				pEnvPtr->DevCallBack(pEnvPtr);
++				/* We reuse the same envelope until the complete message has been received ? */
++ //				wscEnvelopeFree(pEnvPtr);
++			}
++			else if(pEnvPtr->callBack != NULL)
++			{
++				pEnvPtr->callBack(pEnvPtr->pMsgPtr, pEnvPtr->msgLen);
++				wscEnvelopeFree(pEnvPtr);
++			} 
++		}
++		else 
++		{
++			pthread_mutex_lock(&gActiveMsgQLock);
++			gActiveMsgQ[workQIdx].pEnvelope = NULL;
++			gActiveMsgQ[workQIdx].used = 0;
++			pthread_mutex_unlock(&gActiveMsgQLock);
++			
++			//Call handler
++			wscActiveMsgHandler(pEnvPtr);
++
++			//After handle this message, remove it from ActiveMsgQ.
++			wscEnvelopeFree(pEnvPtr);
++		}
++	}
++
++	return 0;	
++}
++
++
++/********************************************************************************
++ * WscMsgQVerifyTimeouts
++ *
++ * Description: 
++ *       Checks if the envelope in  MsgQ(active and passive) was expired. 
++ *		 If the envelope expires, this handle is removed from the list and trigger the 
++ *			corresponding handler handle it.
++ *
++ * Parameters:
++ *    incr -- The increment to subtract from the timeouts each time the
++ *            function is called.
++ *
++ ********************************************************************************/
++void WscMsgQVerifyTimeouts(int incr)
++{
++	int idx;
++	WscEnvelope *envelope = NULL;
++
++	//First check the passive queue!
++	for(idx = 0; idx < WSCPASSIVEMSGQ_MAX_ENTRY; idx++)
++	{
++		if(gPassiveMsgQ[idx].used == 1 && ((envelope = gPassiveMsgQ[idx].pEnvelope) != NULL))
++		{
++			envelope->timerCount -= incr;
++			if(envelope->timerCount <= 0)
++			{
++				DBGPRINTF(RT_DBG_INFO, "WscMsgQVerifyTimeouts(): PassiveQIdx(%d) timeout happened, eventID=0x%x!\n", 
++						idx, envelope->envID);
++				if((envelope->callBack != NULL) || (envelope->DevCallBack != NULL))
++				{
++					wscEnvelopeFree(envelope);
++					envelope = NULL;
++				}
++				else 
++				{
++					envelope->flag = WSC_ENVELOPE_TIME_OUT;
++				}
++				gPassiveMsgQ[idx].used = 0;
++				gPassiveMsgQ[idx].pEnvelope = NULL;
++			}
++		}
++	}
++
++
++	// Check the Active Queue.
++	pthread_mutex_lock(&gActiveMsgQLock);
++	for(idx = 0; idx < WSCACTIVEMSGQ_MAX_ENTRY; idx++)
++	{
++		if((gActiveMsgQ[idx].used == 1) && ((envelope = gActiveMsgQ[idx].pEnvelope) != NULL))
++		{
++			envelope->timerCount -= incr;
++			if(envelope->timerCount <= 0)
++			{
++				DBGPRINTF(RT_DBG_INFO, "WscMsgQVerifyTimeouts(): ActiveQIdx(%d) timeout happened!\n", idx);
++				wscEnvelopeFree(envelope);
++				envelope = NULL;
++				gActiveMsgQ[idx].used = 0;
++				gActiveMsgQ[idx].pEnvelope = NULL;
++			}
++		}
++	}
++	pthread_mutex_unlock(&gActiveMsgQLock);
++
++}
++
++static inline int WscMsgQRemoveAll(void)
++{
++	// Just set all queueed envelope as expired!
++	WscMsgQVerifyTimeouts(DEF_WSC_ENVELOPE_TIME_OUT);
++	return 0;
++}
++
++
++int wscMsgSubSystemStop()
++{
++	WscMsgQRemoveAll();
++	pthread_mutex_destroy(&gActiveMsgQLock);
++	return 0;	
++}
++
++
++int wscMsgSubSystemInit(void)
++{
++	time_t nowTime;
++
++
++	// Initialize the Passive message Queue.
++	if ((time(&nowTime)) != ((time_t)-1))
++	{
++		gPassiveMsgID = (uint32)nowTime;
++		memset(gPassiveMsgQ, 0, sizeof(WscU2KMsgQ)* WSCPASSIVEMSGQ_MAX_ENTRY);
++		DBGPRINTF(RT_DBG_INFO, "gPassiveMsgQ Init success! gPassiveMsgID=0x%x!\n", gPassiveMsgID);
++	}
++		
++	// Initialize the Active message Queue system.
++	if(pthread_mutex_init(&gActiveMsgQLock, NULL) != 0) 
++	{
++		goto FailedActiveQ;
++	} else {
++		pthread_mutex_lock(&gActiveMsgQLock);
++	memset(gActiveMsgQ, 0, sizeof(WscU2KMsgQ)* WSCACTIVEMSGQ_MAX_ENTRY);
++	DBGPRINTF(RT_DBG_INFO, "gActiveMsgQ Init success!\n");
++		pthread_mutex_unlock(&gActiveMsgQLock);
++	}
++
++	return WSC_SYS_SUCCESS;
++FailedActiveQ:
++	pthread_mutex_destroy(&gActiveMsgQLock);
++Failed:
++	return WSC_SYS_ERROR; 
++
++}
++
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_msg.h
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_msg.h
+@@ -0,0 +1,114 @@
++#ifndef __WSC_MSG_H__
++#define __WSC_MSG_H__
++
++#include "wsc_common.h"
++
++#ifndef IN
++#define IN
++#endif
++
++#ifndef OUT
++#define OUT
++#endif
++
++#ifndef INOUT
++#define INOUT
++#endif
++
++#define WSC_OPCODE_UPNP_MASK	0x10
++#define WSC_OPCODE_UPNP_DATA	0x11
++#define WSC_OPCODE_UPNP_MGMT	0x12
++#define WSC_OPCODE_UPNP_CTRL	0x13
++
++#define WSC_UPNP_MGMT_SUB_PROBE_REQ		0x01
++#define WSC_UPNP_MGMT_SUB_CONFIG_REQ	0x02
++#define WSC_UPNP_MGMT_SUB_REG_SELECT	0x03
++
++//patch for Atheros External registrar
++#define WSC_UPNP_DATA_SUB_INCLUDE_MAC	0x0100
++
++#define WSC_UPNP_DATA_SUB_NORMAL		0x00
++#define WSC_UPNP_DATA_SUB_TO_ALL		0x01
++#define WSC_UPNP_DATA_SUB_TO_ONE		0x02
++#define WSC_UPNP_DATA_SUB_ACK			0x03
++#define WSC_UPNP_DATA_SUB_M1			0x04
++#define WSC_UPNP_DATA_SUB_M2			0x05
++#define WSC_UPNP_DATA_SUB_M2D			0x06
++#define WSC_UPNP_DATA_SUB_M3			0x07
++#define WSC_UPNP_DATA_SUB_M4			0x08
++#define WSC_UPNP_DATA_SUB_M5			0x09
++#define WSC_UPNP_DATA_SUB_M6			0x0A
++#define WSC_UPNP_DATA_SUB_M7			0x0B
++#define WSC_UPNP_DATA_SUB_M8			0x0C
++#define WSC_UPNP_DATA_SUB_WSC_ACK		0x0D
++#define WSC_UPNP_DATA_SUB_WSC_NACK		0x0E
++#define WSC_UPNP_DATA_SUB_WSC_DONE		0x0F
++#define WSC_UPNP_DATA_SUB_WSC_UNKNOWN	0xff
++
++#define WSC_ENVELOPE_NONE			0x0
++#define WSC_ENVELOPE_SUCCESS		0x1
++#define WSC_ENVELOPE_MEM_FAILED		0x2
++#define WSC_ENVELOPE_TIME_OUT		0x3
++/*
++	This data structrue used for communication between UPnP threads and netlink thread.
++	The UPnP thread create a 
++*/
++#define Q_TYPE_ACTIVE	1
++#define Q_TYPE_PASSIVE	2
++
++#define DEF_WSC_ENVELOPE_TIME_OUT	30
++typedef int (*envCallBack)(char *msg, int msgLen);
++
++/* for compile issue */
++struct _WscEnvelope;
++
++typedef int (*httpCallBack)(struct _WscEnvelope *pEnvelope);
++
++typedef struct _WscEnvelope{
++	uint32		envID;			//envID = 0 was reserved for msg directly send from kernel space. other value means send by upnp daemon.
++	void 		*pMsgPtr;
++	int 		msgLen;
++	int 		actLen;
++	int 		flag;
++	int			timerCount;
++	struct upnphttp * h;
++	httpCallBack DevCallBack;
++	envCallBack callBack;		//Used for UPnP Control Point.
++}WscEnvelope;
++
++typedef struct _WscU2KMsgQ{
++	WscEnvelope *pEnvelope;
++	int used;
++}WscU2KMsgQ;
++
++typedef int (*msgHandleCallback)(char *pMsgBuf, int msgLen);
++typedef struct wscMsgHandle{
++	uint32 msgType;
++	msgHandleCallback handle;
++}WscMsgHandle;
++
++int wscEnvelopeFree(IN WscEnvelope *envelope);
++
++WscEnvelope *wscEnvelopeCreate(void);
++
++int wscEnvelopeRemove(
++	IN WscEnvelope *envelope,
++	OUT int qIdx);
++
++int wscMsgQInsert(
++	IN WscEnvelope *envelope,
++	OUT int *pQIdx);
++
++int wscMsgQRemove(
++	IN int qType, 
++	IN int qIdx);
++
++int WscRTMPMsgHandler(
++	IN char *pBuf,
++	IN int len);
++
++int wscMsgSubSystemStop(void);
++
++int wscMsgSubSystemInit(void);
++
++#endif //endif of "#ifndef __WSC_MSG_H__"
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_netlink.c
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_netlink.c
+@@ -0,0 +1,229 @@
++/*
++	function handler for linux netlink socket!
++ */
++#include <unistd.h>
++#include <fcntl.h>
++#include <asm/types.h>
++#include <sys/socket.h>
++#include <linux/netlink.h>
++#include <linux/rtnetlink.h>
++#include "wsc_common.h"
++#include "wsc_netlink.h"
++#include "wsc_msg.h"
++
++int we_version_compiled = WIRELESS_EXT;
++/*static*/ int netlink_sock = -1;
++
++static void wscEventHandler(char *data, int len)
++{
++	struct iw_event iwe_buf, *iwe = &iwe_buf;
++	char *pos, *end, *custom;
++//	char *buf;
++
++	pos = data;
++	end = data + len;
++
++	//wsc_hexdump("wscEventHandler", pos, len);
++
++	while (pos + IW_EV_LCP_LEN <= end)
++	{
++		/* Event data may be unaligned, so make a local, aligned copy
++		 * before processing. */
++		memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
++		//DBGPRINTF(RT_DBG_INFO, "Wireless event: cmd=0x%x len=%d, we_version_compiled=%d!\n", iwe->cmd, iwe->len, we_version_compiled);
++		if (iwe->len <= IW_EV_LCP_LEN)
++			return;
++
++		custom = pos + IW_EV_POINT_LEN;
++		if(iwe->cmd == IWEVCUSTOM)
++		{
++			if (we_version_compiled > 18)
++			{			
++				/* WE-19 removed the pointer from struct iw_point */
++				char *dpos = (char *) &iwe_buf.u.data.length;
++				int dlen = dpos - (char *) &iwe_buf;
++				memcpy(dpos, pos + IW_EV_LCP_LEN, sizeof(struct iw_event) - dlen);
++			}
++			else
++			{
++				memcpy(&iwe_buf, pos, sizeof(struct iw_event));
++			}	
++			//wsc_hexdump("After Stripped", custom, len - IW_EV_POINT_LEN);
++
++			if (custom + iwe->u.data.length > end){
++				DBGPRINTF(RT_DBG_ERROR, "custom(0x%x) + iwe->u.data.length(0x%x >end!\n", custom, iwe->u.data.length);
++				return;
++			}
++#if 0
++			buf = malloc(iwe->u.data.length + 1);
++			if (buf == NULL)
++				return;
++				
++			memcpy(buf, custom, iwe->u.data.length);
++			buf[iwe->u.data.length] = '\0';
++			DBGPRINTF(RT_DBG_INFO, "iwe->u.data.flags=0x%x!\n", iwe->u.data.flags);
++#endif
++			switch(iwe->u.data.flags)
++			{
++				case RT_ASSOC_EVENT_FLAG:
++				case RT_DISASSOC_EVENT_FLAG:
++				case RT_REQIE_EVENT_FLAG:
++				case RT_RESPIE_EVENT_FLAG:
++				case RT_ASSOCINFO_EVENT_FLAG:
++				case RT_PMKIDCAND_FLAG:
++					break;
++				default:
++					if(strncmp(custom, "RAWSCMSG", 8) == 0)
++					{	
++						DBGPRINTF(RT_DBG_LOUD, "Recevive a RAWSCMSG segment\n");
++						WscRTMPMsgHandler(custom, iwe->u.data.length);
++					}
++					break;
++			}
++//			free(buf);
++		}
++		pos += iwe->len;
++	}
++	
++}
++
++
++static void wscNLEventRTMNewlinkHandle(struct nlmsghdr *nlmsgHdr, int len)
++{
++	struct ifinfomsg *ifi;
++	int attrlen, nlmsg_len, rta_len;
++	struct rtattr * attr;
++
++	DBGPRINTF(RT_DBG_LOUD, "%s", __FUNCTION__);
++
++	if (len < sizeof(struct ifinfomsg))
++		return;
++
++	ifi = NLMSG_DATA(nlmsgHdr);
++    //wsc_hexdump("ifi: ", (char *)ifi, sizeof(struct ifinfomsg));
++	
++	nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
++       
++	attrlen = nlmsgHdr->nlmsg_len - nlmsg_len;
++	DBGPRINTF(RT_DBG_LOUD, "attrlen=%d\n",attrlen);
++	if (attrlen < 0)
++		return;
++
++	attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
++    //wsc_hexdump("rtattr: ", (char *)attr,sizeof(struct rtattr));
++	rta_len = RTA_ALIGN(sizeof(struct rtattr));
++	//wsc_hexdump("rtattr: ", (char *)attr, rta_len);
++	while (RTA_OK(attr, attrlen))
++	{
++		if (attr->rta_type == IFLA_WIRELESS)
++		{
++			wscEventHandler(((char *) attr) + rta_len, attr->rta_len - rta_len);
++		} 
++		attr = RTA_NEXT(attr, attrlen);
++		//wsc_hexdump("rta_type: ", (char *)attr,sizeof(struct rtattr));
++	}
++}
++
++void wscNLSockRecv(int sock)
++{
++	char buf[2000];// 8192 ==> 2000
++	int left;
++	struct sockaddr_nl from;
++	socklen_t fromlen;
++	struct nlmsghdr *nlmsgHdr;
++
++	fromlen = sizeof(from);
++	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *)&from, &fromlen);
++
++	if (left < 0)
++	{
++		if (errno != EINTR && errno != EAGAIN)
++			perror("recvfrom(netlink)");
++		return;
++	}
++
++	nlmsgHdr = (struct nlmsghdr *)buf;
++	//wsc_hexdump("nlmsgHdr: ", (char *)nlmsgHdr, nlmsgHdr->nlmsg_len);
++
++	while (left >= sizeof(*nlmsgHdr))
++	{
++		int len, plen;
++
++		len = nlmsgHdr->nlmsg_len;
++		plen = len - sizeof(*nlmsgHdr);
++		if (len > left || plen < 0)
++		{
++			DBGPRINTF(RT_DBG_LOUD, "Malformed netlink message: len=%d left=%d plen=%d", len, left, plen);
++			break;
++		}
++
++		switch (nlmsgHdr->nlmsg_type)
++		{
++			case RTM_NEWLINK:
++				wscNLEventRTMNewlinkHandle(nlmsgHdr, plen);
++				break;
++		}
++
++		len = NLMSG_ALIGN(len);
++		left -= len;
++		nlmsgHdr = (struct nlmsghdr *) ((char *) nlmsgHdr + len);
++	}
++
++	if (left > 0)
++	{
++		DBGPRINTF(RT_DBG_INFO, "%d extra bytes in the end of netlink message", left);
++	}
++
++}
++
++
++/******************************************************************************
++ * wscK2UModuleInit
++ *
++ * Description: 
++ *       The Kernel space 2 user space msg subsystem entry point.
++ *	  In Linux system, we use netlink socket to receive the specific type msg
++ *	  send from wireless driver.
++ *		 This function mainly create a posix thread and recvive msg then dispatch
++ *	  to coressponding handler.
++ *
++ * Parameters:
++ *    None
++ * 
++ * Return:
++ *    success: 1
++ *    fail   : 0
++ *****************************************************************************/
++int wscK2UModuleInit(void)
++{
++	int sock = -1;
++    struct sockaddr_nl local;
++	
++	//Create netlink socket
++	sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
++	if (sock < 0)
++	{
++		perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
++		return WSC_SYS_ERROR;
++	}
++
++	memset(&local, 0, sizeof(local));
++	local.nl_family = AF_NETLINK;
++	local.nl_groups = RTMGRP_LINK;
++
++	if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0)
++	{
++		perror("bind(netlink)");
++		close(sock);
++		return WSC_SYS_ERROR;
++	}
++
++    /* 
++    	start a netlink socket receiver handle thread  
++    */
++    DBGPRINTF(RT_DBG_INFO, "sock=%d!(0x%p)\n", sock, &sock);
++	netlink_sock = sock;
++	
++	return WSC_SYS_SUCCESS;
++}
++
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_netlink.h
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_netlink.h
+@@ -0,0 +1,94 @@
++/*
++	header file for wsc_netlink.c
++*/
++
++#ifndef __WSC_NETLINK_H__
++#define __WSC_NETLINK_H__
++
++#include <linux/types.h>
++#include <linux/if.h>
++#include <linux/wireless.h>
++
++#define NETLINK_ROUTE 0
++#define RTMGRP_LINK 1
++#ifndef RTM_BASE
++#define RTM_BASE 0x10
++#endif
++
++//extern int netlink_sock;
++
++#if 0	//already defined in <linux/rtnetlink.h>
++#define RTM_NEWLINK (RTM_BASE + 0)
++#define RTM_DELLINK (RTM_BASE + 1)
++
++#define RTA_ALIGNTO 4
++#define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1))
++#define RTA_NEXT(rta,attrlen) \
++	((attrlen) -= RTA_ALIGN((rta)->rta_len), \
++	(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
++
++#define NLMSG_ALIGNTO 4
++#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1))
++#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr)))
++#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0)))
++#endif
++
++#ifndef RTA_OK
++#define RTA_OK(rta,len) \
++	((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \
++	(rta)->rta_len <= (len))
++#endif
++
++#if (WIRELESS_EXT < 15)
++/* ----------------------- WIRELESS EVENTS ----------------------- */
++/*
++ * Wireless events are carried through the rtnetlink socket to user
++ * space. They are encapsulated in the IFLA_WIRELESS field of
++ * a RTM_NEWLINK message.
++ */
++
++/*
++ * A Wireless Event. Contains basically the same data as the ioctl...
++ */
++struct iw_event
++{
++	unsigned short		len;            /* Real lenght of this stuff */
++    unsigned short		cmd;            /* Wireless IOCTL */
++	union iwreq_data	u;              /* IOCTL fixed payload */
++};
++
++/* Size of the Event prefix (including padding and alignement junk) */
++#define IW_EV_LCP_LEN   (sizeof(struct iw_event) - sizeof(union iwreq_data))
++
++/* Size of the various events */
++#define IW_EV_CHAR_LEN  (IW_EV_LCP_LEN + IFNAMSIZ)
++#define IW_EV_UINT_LEN  (IW_EV_LCP_LEN + sizeof(__u32))
++#define IW_EV_FREQ_LEN  (IW_EV_LCP_LEN + sizeof(struct iw_freq))
++#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point))
++#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param))
++#define IW_EV_ADDR_LEN  (IW_EV_LCP_LEN + sizeof(struct sockaddr))
++#define IW_EV_QUAL_LEN  (IW_EV_LCP_LEN + sizeof(struct iw_quality))
++
++/* Note : in the case of iw_point, the extra data will come at the
++ * end of the event */
++
++#endif  /* _LINUX_WIRELESS_H */
++
++
++#define IWEVCUSTOM	0x8C02		/* Driver specific ascii string */
++#define IWEVFIRST	0x8C00
++#define IW_EVENT_IDX(cmd)	((cmd) - IWEVFIRST)
++
++/* iw_point events are special. First, the payload (extra data) come at
++ * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second,
++ * we omit the pointer, so start at an offset. */
++#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - (char *) NULL)
++
++//wpa_supplicant event flags.
++#define	RT_ASSOC_EVENT_FLAG                         0x0101
++#define	RT_DISASSOC_EVENT_FLAG                      0x0102
++#define	RT_REQIE_EVENT_FLAG                         0x0103
++#define	RT_RESPIE_EVENT_FLAG                        0x0104
++#define	RT_ASSOCINFO_EVENT_FLAG                     0x0105
++#define RT_PMKIDCAND_FLAG                           0x0106
++#endif
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_upnp.h
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_upnp.h
+@@ -0,0 +1,163 @@
++/*
++
++
++*/
++#ifndef __WSC_UPNP_H__
++#define __WSC_UPNP_H__
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#ifndef IN
++#define IN
++#endif
++
++#ifndef OUT
++#define OUT
++#endif
++
++#ifndef INOUT
++#define INOUT
++#endif
++
++#define WSC_UPNP_DESC_URL_LEN	200	
++//peter : 0523
++//extern char HostDescURL[WSC_UPNP_DESC_URL_LEN];	// Used to save the DescriptionURL of local host.
++extern unsigned int HostIPAddr;						// Used to save the binded IP address of local host.
++/* 
++  Wsc Event format and Service State variable names, values, and defaults.
++	WLANEvent: 
++		This state variable event indicates that a WCN-NET Probe-Request or an 802.1X/WCN-NET 
++		EAP frame was received on the 802.11 network. The proxy service issues the event.
++	APStatus:
++		This state variable event indicates that the AP has either had a configuration change 
++		or that the AP has had too many failed authentications against its PIN/password and has 
++		locked itself out of Enrollee mode.
++	STAStatus:
++		This state variable event indicates that the STA has either had a configuration change 
++		or that the STA has had too many failed authentications against its PIN/password and 
++		has locked itself out of Enrollee mode.
++*/
++#define WSC_STATE_VAR_COUNT		3
++#define WSC_STATE_VAR_MAXVARS	WSC_STATE_VAR_COUNT
++// Max value length. TODO: shiang: shall we need this??
++#define WSC_STATE_VAR_MAX_STR_LEN 1024 
++
++#define WSC_EVENT_WLANEVENT		0		// Index of "WLANEvent" State variable// shiang
++#define WSC_EVENT_APSTATUS		1		// Index of "APStatus" State variable
++#define WSC_EVENT_STASTATUS		2		// Index of "STAStatus" State variable
++
++
++/*****************************************************************************
++	Wsc Control Point Related data structures.
++ *****************************************************************************/
++#define NAME_SIZE  256	//refer to UPnP Library
++struct upnpService{
++	char ServiceId[NAME_SIZE];			//Service identification
++	char ServiceType[NAME_SIZE];		//Service type.
++	char SCPDURL[NAME_SIZE];			//URL to service description
++	char EventURL[NAME_SIZE];			//URL for eventing
++	char ControlURL[NAME_SIZE];			//URL for control
++    char SID[NAME_SIZE];				//Subscribe Identifier
++	char *StateVarVal[WSC_STATE_VAR_COUNT];
++	struct upnpService *next;			//Pointer to next service.
++};
++
++/*
++	The following data structure used to save the UPnP Device which
++	support WSC.
++*/
++
++struct upnpDevice
++{
++	char UDN[NAME_SIZE];				//uuid
++	char DescDocURL[NAME_SIZE];			//URL of the device description file
++	char FriendlyName[NAME_SIZE];		//short user-friendly title
++	char PresURL[NAME_SIZE];			//URL for presentation
++	int  AdvrTimeOut;					//Device Advertisement timeout
++	int timeCount;
++	unsigned int ipAddr;				//IP address of this device
++	struct upnpService services;		//Wsc Device Service related info
++};
++
++//typedef struct upnpDevice upnpDevice_t;
++
++struct upnpDeviceNode {
++	struct upnpDevice device;
++	struct upnpDeviceNode *next;
++};
++
++
++/*
++	The following data structure used to save the UPnP Contorl Point 
++	which subscribe to us.
++*/
++struct upnpCtrlPoint
++{
++	char SubURL[NAME_SIZE];				//URL for subscription
++	char SID[NAME_SIZE];				//Subscription ID of the Control Point
++	int  SubTimeOut;					//Subscription time-out
++	unsigned int ipAddr;				//IP address of this control point
++};
++
++struct upnpCPNode {
++	struct upnpCtrlPoint device;
++	struct upnpCPNode *next;
++};
++
++
++/* Device type for wsc device  */
++#define WscDeviceTypeStr	"urn:schemas-wifialliance-org:device:WFADevice:1"
++
++/* Service type for wsc services */
++#define WscServiceTypeStr	"urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
++
++/* Service Id for wsc services */
++#define WscServiceIDStr		"urn:wifialliance-org:serviceId:WFAWLANConfig1"
++
++typedef enum{
++	ERR_UPNP_WSC_ACTION_ACT = 401,
++	ERR_UPNP_WSC_ACTION_ARG = 402,
++	ERR_UPNP_WSC_ACTION_VAR = 403,
++	ERR_UPNP_WSC_ACTION_FAILED = 501,
++}WSC_UPNP_ACTION_ERRCODE;
++
++
++int WscEventCtrlMsgRecv(
++	IN char *pBuf,
++	IN int  bufLen);
++
++int WscEventDataMsgRecv(
++	IN char *pBuf,
++	IN int  bufLen);
++
++int WscEventMgmtMsgRecv(
++	IN char *pBuf,
++	IN int  bufLen);
++
++int wscU2KMsgCreate(
++	INOUT char **dstPtr,
++	IN char *srcPtr,
++	IN int msgLen,
++	IN int EAPType);
++
++
++int WscUPnPDevStop(void);
++int WscUPnPDevStart(
++	IN char *ipAddr,
++	IN unsigned short port,
++	IN char *descDoc,
++	IN char *webRootDir);
++
++
++int WscUPnPCPStop(void);
++int WscUPnPCPStart(
++	IN char *ip_address,
++	IN unsigned short port);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_upnp_cp.c
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_upnp_cp.c
+@@ -0,0 +1,1368 @@
++/*
++	wsc UPnP Contorl Point routines
++
++
++*/
++#include <stdio.h>
++#include <string.h>
++#include "sample_util.h"
++#include "wsc_common.h"
++#include "wsc_msg.h"
++#include "wsc_upnp.h"
++#include "wsc_ioctl.h"
++#include "upnp.h"
++#include "ixmlparser.h"
++
++static UpnpClient_Handle WscCPHandle = -1;
++
++/* Global arrays for storing variable names and counts for Wsc services */
++static char *WscVarName[WSC_STATE_VAR_COUNT] = {"WLANEvent", "APStatus", "STAStatus"};
++
++/* Timeout to request during subscriptions */
++#define DEF_SUBSCRIPTION_TIMEOUT	1801
++
++/* The first node in the global device list, or NULL if empty */
++struct upnpDeviceNode *WscDeviceList = NULL;
++
++
++
++int WscUPnPCPDeviceHandler(
++	IN Upnp_EventType EventType,
++	IN void *Event,
++	IN void *Cookie);
++
++	
++/********************************************************************************
++ * WscUPnPCPDumpList
++ *
++ * Description: 
++ *       Print the universal device names for each device in the Wsc device list
++ *
++ * Parameters:
++ *   None
++ *
++ ********************************************************************************/
++int WscUPnPCPDumpList(void)
++{
++	struct upnpDeviceNode *nodePtr;
++	struct upnpDevice *dev;
++	struct upnpService *service;
++	int i = 0;
++
++	printf("WscUPnPCPDumpList:\n");
++	nodePtr = WscDeviceList;
++	while (nodePtr) 
++	{
++		dev = &nodePtr->device;
++		service = &dev->services;
++		
++		printf("UPnPDevice No.%3d:\n", ++i);
++		printf("\tDevice:\n");
++		printf("\t\tFriendlyName=%s!\n", dev->FriendlyName);
++		printf("\t\tDescDocURL=%s!\n", dev->DescDocURL);
++		printf("\t\tUDN=%s!\n", dev->UDN);
++		printf("\t\tPresURL=%s!\n", dev->PresURL);
++		printf("\t\tIP Address=0x%x!\n", dev->ipAddr);
++		printf("\t\tAdvrTimeOut=%d!\n", dev->AdvrTimeOut);
++		printf("\tService:\n");
++		printf("\t\tservIdStr=%s!\n", service->ServiceId);
++		printf("\t\tservTypeStr=%s!\n", service->ServiceType);
++		printf("\t\tscpdURL=%s!\n", service->SCPDURL);
++		printf("\t\teventURL=%s!\n", service->EventURL);
++		printf("\t\tcontrolURL=%s!\n", service->ControlURL);
++		printf("\t\tsubscribeID=%s!\n", service->SID);
++		
++		nodePtr = nodePtr->next;
++	}
++	printf("\n");
++
++	return WSC_SYS_SUCCESS;
++}
++
++
++/********************************************************************************
++ * WscUPnPCPDeleteNode
++ *
++ * Description: 
++ *       Delete a device node from the Wsc device list.  Note that this function is 
++ *       NOT thread safe, and should be called from another function that has already 
++ *       locked the Wsc device list.
++ *
++ * Parameters:
++ *   node -- The device node
++ *
++ * Return:
++ *    WSC_SYS_SUCCESS - if success
++ *    WSC_SYS_ERROR   - if failure
++ ********************************************************************************/
++int WscUPnPCPDeleteNode(
++	IN struct upnpDeviceNode *node)
++{
++	int rc, var;
++
++	if (NULL == node) 
++	{
++		DBGPRINTF(RT_DBG_ERROR, "ERROR: WscUPnPCPDeleteNode: Node is empty\n");
++		return WSC_SYS_ERROR;
++	}
++
++	/* If we have a valid control SID, then unsubscribe */
++	if (strcmp(node->device.services.SID, "") != 0) 
++	{
++		rc = UpnpUnSubscribe(WscCPHandle, node->device.services.SID);
++		if (rc == 0) 
++			DBGPRINTF(RT_DBG_INFO, "Unsubscribed from WscService EventURL with SID=%s\n", node->device.services.SID);
++		else
++			DBGPRINTF(RT_DBG_ERROR, "Error unsubscribing to WscService EventURL -- %d\n", rc);
++	}
++			
++	for (var = 0; var < WSC_STATE_VAR_COUNT; var++) 
++	{
++		if (node->device.services.StateVarVal[var])
++			free(node->device.services.StateVarVal[var]);
++	}
++
++	//Notify New Device Added
++//	WscCPDevUpdate(NULL, NULL, node->device.UDN, DEVICE_REMOVED);
++	free(node);
++	node = NULL;
++
++	return WSC_SYS_SUCCESS;
++}
++
++
++/********************************************************************************
++ * WscUPnPCPRemoveDevice
++ *
++ * Description: 
++ *       Remove a device from the wsc device list.
++ *
++ * Parameters:
++ *   UDN -- The Unique Device Name for the device to remove
++ *
++ * Return:
++ * 		Always success
++ ********************************************************************************/
++int WscUPnPCPRemoveDevice(IN char *UDN)
++{
++	struct upnpDeviceNode *devNodePtr, *prev;
++
++	devNodePtr = WscDeviceList;
++	if (!devNodePtr)
++	{
++		DBGPRINTF(RT_DBG_INFO, "WARNING: WscUPnPCPRemoveDevice: Device list empty\n");
++	}
++	else
++	{
++		if (strcmp(devNodePtr->device.UDN, UDN) == 0)
++		{
++			WscDeviceList = devNodePtr->next;
++			WscUPnPCPDeleteNode(devNodePtr);
++		}
++		else
++		{
++			prev = devNodePtr;
++			devNodePtr = devNodePtr->next;
++
++			while (devNodePtr)
++			{
++				if (strcmp(devNodePtr->device.UDN, UDN) == 0)
++				{
++					prev->next = devNodePtr->next;
++					WscUPnPCPDeleteNode(devNodePtr);
++					break;
++				}
++
++				prev = devNodePtr;
++				devNodePtr = devNodePtr->next;
++			}
++		}
++	}
++
++	return WSC_SYS_SUCCESS;
++}
++
++
++/********************************************************************************
++ * WscUPnPCPRemoveAll
++ *
++ * Description: 
++ * 		Remove all devices from the wsc device list.
++ *
++ * Parameters:
++ *   	void
++ *
++ * Return:
++ * 		Always success
++ ********************************************************************************/
++int WscUPnPCPRemoveAll(void)
++{
++	struct upnpDeviceNode *devNodePtr, *next;
++
++	devNodePtr = WscDeviceList;
++	WscDeviceList = NULL;
++
++	while(devNodePtr)
++	{
++		next = devNodePtr->next;
++		WscUPnPCPDeleteNode(devNodePtr);
++		devNodePtr = next;
++	}
++
++	return WSC_SYS_SUCCESS;
++}
++
++
++int WscUPnPCPCheckService(
++	IN IXML_Document * DescDoc)
++{
++	// TODO:Check if the SCPD description XML file has correct format
++	return 0;
++}
++
++
++/********************************************************************************
++ * WscUPnPCPAddDevice
++ *
++ * Description: 
++ *       Add a new Wsc device into the wsc device list or just update its advertisement 
++ *       expiration timeout if it alreay exists in the list.
++ *
++ * Parameters:
++ *   	deviceType -- The device type want to add to the Device List
++ *   	d_event -- The "Upnp_Discovery" data
++ *
++ * Return:
++ *	 	WSC_SYS_SUCCESS - if success
++ *   	other value     - if failure
++ ********************************************************************************/
++int WscUPnPCPAddDevice(
++	IN char *deviceType,
++	IN struct Upnp_Discovery *d_event,
++	OUT unsigned int *outIPAddr)
++{
++	IXML_Document *DescDoc = NULL, *scpdDescDoc = NULL;
++	char *deviceTypeStr = NULL, *friendlyName = NULL, *baseURL = NULL, *relURL = NULL, *UDN = NULL;
++	char presURL[200] = {0};
++	char *serviceId = NULL, *SCPDURL = NULL, *eventURL = NULL, *controlURL = NULL;
++	Upnp_SID eventSID;
++	struct upnpDeviceNode *newDeviceNode, *devNodePtr;
++	int ret = WSC_SYS_ERROR;
++	int found = 0;
++	int TimeOut = DEF_SUBSCRIPTION_TIMEOUT;
++	unsigned int ipAddr = 0;
++	unsigned short port = 0;
++
++	if(d_event->DestAddr != NULL)
++	{
++		ipAddr = (unsigned int)d_event->DestAddr->sin_addr.s_addr;
++		port = (unsigned short)d_event->DestAddr->sin_port;
++		*outIPAddr = ipAddr;
++	}
++	
++	printf("%s():outIPAddr=0x%x!\n", __FUNCTION__, *outIPAddr);
++	if( !(strcmp(&HostDescURL[0], &d_event->Location[0])))
++	{
++//		DBGPRINTF(RT_DBG_INFO, "%s():Adver from LocalDev, ignore it!\n", __FUNCTION__);
++		goto done;
++	}
++		
++	// Check if this device is already in the list
++	devNodePtr = WscDeviceList;
++	while (devNodePtr)
++	{
++		if((strcmp(devNodePtr->device.UDN, d_event->DeviceId) == 0) 
++			&& (strcmp(devNodePtr->device.DescDocURL, d_event->Location) == 0))
++		{
++			found = 1;
++			break;
++		}
++		devNodePtr = devNodePtr->next;
++	}
++
++	if (found)
++	{
++		// Update the advertisement timeout field
++//		DBGPRINTF(RT_DBG_INFO, "Old device, just update the expires!\n");
++		devNodePtr->device.AdvrTimeOut = d_event->Expires;
++		devNodePtr->device.timeCount = 0;
++	} else {
++		DBGPRINTF(RT_DBG_INFO, "Adver from a new device!\n");
++		if((ret = UpnpDownloadXmlDoc(d_event->Location, &DescDoc)) != UPNP_E_SUCCESS){
++			DBGPRINTF(RT_DBG_ERROR, "Get device description failed from %s -- err=%d\n", d_event->Location, ret);
++			goto done;
++		}
++
++		/* Read key elements from description document */
++		UDN = SampleUtil_GetFirstDocumentItem(DescDoc, "UDN");
++		deviceTypeStr = SampleUtil_GetFirstDocumentItem(DescDoc, "deviceType");
++		friendlyName = SampleUtil_GetFirstDocumentItem(DescDoc, "friendlyName");
++		baseURL = SampleUtil_GetFirstDocumentItem(DescDoc, "URLBase");
++		relURL = SampleUtil_GetFirstDocumentItem(DescDoc, "presentationURL");
++
++		if((UDN == NULL) || (deviceTypeStr == NULL) || (friendlyName == NULL))
++		{
++			DBGPRINTF(RT_DBG_ERROR, "%s(): Get UDN failed!\n", __FUNCTION__);
++			goto done;
++		}
++		UpnpResolveURL((baseURL ? baseURL : d_event->Location), relURL, presURL);
++	
++		if (SampleUtil_FindAndParseService(DescDoc, d_event->Location, WscServiceTypeStr,
++											&serviceId, &SCPDURL, &eventURL,&controlURL))
++		{
++			if (SCPDURL != NULL)
++			{	
++				if ((ret = UpnpDownloadXmlDoc(SCPDURL, &scpdDescDoc)) != UPNP_E_SUCCESS)
++				{
++					DBGPRINTF(RT_DBG_ERROR, "Get service description failed from %s -- err=%d\n", SCPDURL, ret);
++				} else {
++					WscUPnPCPCheckService(scpdDescDoc);
++					free(scpdDescDoc);
++				}
++			}
++
++			if ((ret = UpnpSubscribe(WscCPHandle, eventURL, &TimeOut, eventSID)) != UPNP_E_SUCCESS)
++			{
++				DBGPRINTF(RT_DBG_ERROR, "Error Subscribing to EventURL(%s) -- %d\n", eventURL, ret);
++				goto done;
++			}
++		
++			/* Create a new device node */
++			newDeviceNode = (struct upnpDeviceNode *)malloc(sizeof(struct upnpDeviceNode));
++			if (newDeviceNode == NULL) {
++				DBGPRINTF(RT_DBG_ERROR, "%s: create new wsc Device Node failed!\n", __FUNCTION__);
++				goto done;
++			}
++			memset(newDeviceNode, 0, sizeof(newDeviceNode));
++			newDeviceNode->device.services.StateVarVal[WSC_EVENT_WLANEVENT] = NULL;
++			newDeviceNode->device.services.StateVarVal[WSC_EVENT_APSTATUS] = NULL;
++			newDeviceNode->device.services.StateVarVal[WSC_EVENT_STASTATUS] = NULL;
++
++			strncpy(newDeviceNode->device.UDN, UDN, NAME_SIZE-1);
++			strncpy(newDeviceNode->device.DescDocURL, d_event->Location, NAME_SIZE-1);
++			strncpy(newDeviceNode->device.FriendlyName, friendlyName, NAME_SIZE-1);
++			strncpy(newDeviceNode->device.PresURL, presURL, NAME_SIZE-1);
++			newDeviceNode->device.AdvrTimeOut = d_event->Expires;
++			newDeviceNode->device.timeCount = 0;
++			newDeviceNode->device.ipAddr = ipAddr;
++			strncpy(newDeviceNode->device.services.ServiceType, WscServiceTypeStr, NAME_SIZE-1);
++			if (serviceId)
++				strncpy(newDeviceNode->device.services.ServiceId, serviceId, NAME_SIZE-1);
++			if (SCPDURL)
++				strncpy(newDeviceNode->device.services.SCPDURL, SCPDURL, NAME_SIZE-1);
++			if (eventURL)
++				strncpy(newDeviceNode->device.services.EventURL, eventURL, NAME_SIZE-1);
++			if (controlURL)
++				strncpy(newDeviceNode->device.services.ControlURL, controlURL, NAME_SIZE-1);
++			if (eventSID)
++				strncpy(newDeviceNode->device.services.SID, eventSID, NAME_SIZE-1);
++
++			newDeviceNode->next = NULL;
++#if 0
++			for (var = 0; var < WSC_STATE_VAR_COUNT; var++)
++			{
++				deviceNode->device.services.serviceList.stateVarVals[var] = (char *)malloc(WSC_STATE_VAR_MAX_STR_LEN);
++				strcpy(deviceNode->device.services.serviceList.stateVarVals[var], "");
++			}
++#endif
++			// Insert the new device node in the list
++			if(WscDeviceList)
++			{
++				newDeviceNode->next = WscDeviceList->next;
++				WscDeviceList->next = newDeviceNode;
++			} else {
++				WscDeviceList = newDeviceNode;
++			}
++
++			ret = WSC_SYS_SUCCESS;
++
++			// TODO:Notify New Device Added
++			//WscCPDevUpdate(NULL, NULL, deviceNode->device.UDN, DEVICE_ADDED);
++		} else {
++			DBGPRINTF(RT_DBG_ERROR, "Error: Could not find Service: %s\n", WscServiceTypeStr);
++		}
++	}
++
++done:
++	if (DescDoc)
++		ixmlDocument_free(DescDoc);
++			
++	if (UDN)
++		free(UDN);
++	if (deviceTypeStr)
++		free(deviceTypeStr);
++    if (friendlyName)
++		free(friendlyName);
++	if (baseURL)
++		free(baseURL);
++	if (relURL)
++		free(relURL);
++
++	if (serviceId)
++		free(serviceId);
++	if (controlURL)
++		free(controlURL);
++	if (eventURL)
++		free(eventURL);
++
++	return ret;
++}
++
++
++/********************************************************************************
++ * WscStateVarUpdate
++ *
++ * Description: 
++ *       Update a Wsc state table.  Called when an event is received.  
++ *
++ * Parameters:
++ *   devNode -- The Wsc Device which need to update the State Variables.
++ *   ChangedStateVars -- DOM document representing the XML received with the event
++ *
++ * Note: 
++ *       This function is NOT thread save.  It must be called from another function 
++ *       that has locked the global device list.
++ ********************************************************************************/
++void WscStateVarUpdate(
++	IN struct upnpDeviceNode *devNode,
++	IN IXML_Document *stateDoc)
++{
++	IXML_NodeList *properties, *stateVars;
++	IXML_Element *propItem, *varItem;
++	int propLen;
++	int i, j;
++	char *stateVal = NULL;
++
++
++	/* Find all of the e:property tags in the document */
++	properties = ixmlDocument_getElementsByTagName(stateDoc, "e:property");
++	if (properties != NULL)
++	{
++		propLen = ixmlNodeList_length(properties);
++		for (i = 0; i < propLen; i++)
++		{ 	/* Loop through each property change found */
++			propItem = (IXML_Element *)ixmlNodeList_item(properties, i);
++			
++			/*
++				For each stateVar name in the state table, check if this
++				is a corresponding property change
++			*/
++			for (j = 0; j < WSC_STATE_VAR_MAXVARS; j++)
++			{
++				stateVars = ixmlElement_getElementsByTagName(propItem, WscVarName[j]);
++				/* If found matched item, extract the value, and update the stateVar table */
++				if (stateVars)
++				{
++					if (ixmlNodeList_length(stateVars))
++					{
++						varItem = (IXML_Element *)ixmlNodeList_item(stateVars, 0);
++						stateVal = SampleUtil_GetElementValue(varItem);
++						if (stateVal)
++						{
++							if (devNode->device.services.StateVarVal[j] != NULL)
++							{
++								//DBGPRINTF(RT_DBG_INFO, "Free the OLD statVarVal!\n"); 
++								// We didn't need do this, because the libupnp will free it.
++								//free(&devNode->device.services.StateVarVal[j]);
++							}
++							devNode->device.services.StateVarVal[j] = stateVal;
++						}
++					}
++
++					ixmlNodeList_free(stateVars);
++					stateVars = NULL;
++				}
++			}
++		}
++		
++		ixmlNodeList_free(properties);
++		
++	}
++}
++
++
++void WscUPnPCPHandleGetVar(
++	IN char *controlURL,
++	IN char *varName,
++	IN DOMString varValue)
++{
++	struct upnpDeviceNode *tmpdevnode;
++
++	tmpdevnode = WscDeviceList;
++	while (tmpdevnode)
++	{
++		if(strcmp(tmpdevnode->device.services.ControlURL, controlURL) == 0)
++		{
++//			SampleUtil_StateUpdate(varName, varValue, tmpdevnode->device.UDN, GET_VAR_COMPLETE);
++			break;
++		}
++		tmpdevnode = tmpdevnode->next;
++	}
++}
++
++
++/********************************************************************************
++ * WscUPnPCPGetDevice
++ *
++ * Description: 
++ *       Given a list number, returns the pointer to the device
++ *       node at that position in the global device list.  Note
++ *       that this function is not thread safe.  It must be called 
++ *       from a function that has locked the global device list.
++ *
++ * Parameters:
++ *   devIPAddr -- The IP address of the device.
++ *   devnode -- The output device node pointer.
++ *
++ ********************************************************************************/
++int WscUPnPCPGetDevice(
++	IN uint32 devIPAddr,
++	IN struct upnpDeviceNode **devnode)
++{
++	struct upnpDeviceNode *devPtr = NULL;
++
++	devPtr = WscDeviceList;
++
++	while (devPtr)
++	{
++		if (devPtr->device.ipAddr == devIPAddr)
++			break;
++		devPtr = devPtr->next;
++	}
++
++	if (!devPtr)
++	{
++		DBGPRINTF(RT_DBG_ERROR, "Didn't find the UPnP Device with IP Address -- 0x%x\n", devIPAddr);
++		return WSC_SYS_ERROR;
++    }
++
++	*devnode = devPtr;
++	
++	return WSC_SYS_SUCCESS;
++}
++
++
++/********************************************************************************
++ * WscUPnPCPSendAction
++ *
++ * Description: 
++ *       Send an Action request to the specified service of a device.
++ *
++ * Parameters:
++ *   devnum -- The number of the device (order in the list,
++ *             starting with 1)
++ *   actionname -- The name of the action.
++ *   param_name -- An array of parameter names
++ *   param_val -- The corresponding parameter values
++ *   param_count -- The number of parameters
++ *
++ ********************************************************************************/
++int WscUPnPCPSendAction(
++	IN uint32 ipAddr,
++	IN char *actionname,
++	IN char **param_name,
++	IN char **param_val,
++	IN int param_count)
++{
++	struct upnpDeviceNode *devnode;
++	IXML_Document *actionNode = NULL;
++	int rc = WSC_SYS_SUCCESS;
++	int param;
++
++    rc = WscUPnPCPGetDevice(ipAddr, &devnode);
++	if (rc == WSC_SYS_SUCCESS)
++	{
++		if (param_count == 0) 
++		{
++			actionNode = UpnpMakeAction(actionname, WscServiceTypeStr, 0, NULL);
++		}
++		else
++		{
++			for (param = 0; param < param_count; param++)
++			{
++				rc = UpnpAddToAction(actionNode, actionname, WscServiceTypeStr, 
++										param_name[param], param_val[param]);
++				if (rc != UPNP_E_SUCCESS ) 
++				{
++					DBGPRINTF(RT_DBG_ERROR, "ERROR: WscUPnPCPSendAction: Trying to add action param, rc=%d!\n", rc);
++					//return -1; // TBD - BAD! leaves mutex locked
++				}
++			}
++		}
++		DBGPRINTF(RT_DBG_INFO, "ControlURL=%s!\n", devnode->device.services.ControlURL);
++		rc = UpnpSendActionAsync( WscCPHandle, devnode->device.services.ControlURL, 
++									WscServiceTypeStr, NULL, actionNode, 
++									WscUPnPCPDeviceHandler, NULL);
++
++        if (rc != UPNP_E_SUCCESS)
++		{
++			DBGPRINTF(RT_DBG_ERROR, "Error in UpnpSendActionAsync -- %d\n", rc);
++			rc = WSC_SYS_ERROR;
++		}
++	}
++	else 
++	{
++		DBGPRINTF(RT_DBG_ERROR, "WscUPnPCPGetDevice failed!\n");
++	}
++	
++	if (actionNode)
++		ixmlDocument_free(actionNode);
++
++	return rc;
++}
++
++
++char *wscCPPutWLANResponseParam[]={"NewMessage", "NewWLANEventType", "NewWLANEventMAC"};
++char *wscCPPutWLANResponseStr[3]={NULL, "2", "00:80:c8:34:c9:56"};
++int wscCPPutWLANResponse(char *msg, int msgLen)
++{
++	unsigned char *encodeStr = NULL;
++	RTMP_WSC_MSG_HDR *rtmpHdr = NULL;
++	int len;
++
++	rtmpHdr = (RTMP_WSC_MSG_HDR *)msg;
++	DBGPRINTF(RT_DBG_INFO, "(%s):Ready to parse the K2U msg(TotalLen=%d, headerLen=%d)!\n", __FUNCTION__, msgLen, sizeof(RTMP_WSC_MSG_HDR));
++	DBGPRINTF(RT_DBG_INFO, "\tMsgType=%d!\n", rtmpHdr->msgType);
++	DBGPRINTF(RT_DBG_INFO, "\tMsgSubType=%d!\n", rtmpHdr->msgSubType);
++	DBGPRINTF(RT_DBG_INFO, "\tipAddress=0x%x!\n", rtmpHdr->ipAddr);
++	DBGPRINTF(RT_DBG_INFO, "\tMsgLen=%d!\n", rtmpHdr->msgLen);
++	
++	if(rtmpHdr->msgType == WSC_OPCODE_UPNP_CTRL || rtmpHdr->msgType == WSC_OPCODE_UPNP_MGMT)
++	{
++		DBGPRINTF(RT_DBG_INFO, "Receive a UPnP Ctrl/Mgmt Msg!\n");
++		return 0;
++	}
++	wsc_hexdump("wscCPPutWLANResponse-K2UMsg", msg, msgLen);
++
++	len = ILibBase64Encode((unsigned char *)(msg + sizeof(RTMP_WSC_MSG_HDR)), rtmpHdr->msgLen, &encodeStr);
++	if (len >0 && encodeStr){
++		wscCPPutWLANResponseStr[0] = (char *)encodeStr;
++		WscUPnPCPSendAction(rtmpHdr->ipAddr, "PutWLANResponse", wscCPPutWLANResponseParam, wscCPPutWLANResponseStr, 3);
++		free(encodeStr);
++	}
++
++	return 0;
++}
++
++
++int wscCPPutMessage(char *msg, int msgLen)
++{
++	unsigned char *encodeStr = NULL;
++	RTMP_WSC_MSG_HDR *rtmpHdr = NULL;
++	int len;
++	char *wscCPStateVarParam="NewInMessage";
++	
++	DBGPRINTF(RT_DBG_INFO, "(%s):Ready to parse the K2U msg(TotalLen=%d, headerLen=%d)!\n", __FUNCTION__, msgLen, sizeof(RTMP_WSC_MSG_HDR));
++	rtmpHdr = (RTMP_WSC_MSG_HDR *)msg;
++	DBGPRINTF(RT_DBG_INFO, "\tMsgType=%d!\n", rtmpHdr->msgType);
++	DBGPRINTF(RT_DBG_INFO, "\tMsgSubType=%d!\n", rtmpHdr->msgSubType);
++	DBGPRINTF(RT_DBG_INFO, "\tipAddress=0x%x!\n", rtmpHdr->ipAddr);
++	DBGPRINTF(RT_DBG_INFO, "\tMsgLen=%d!\n", rtmpHdr->msgLen);
++	
++	if(rtmpHdr->msgType == WSC_OPCODE_UPNP_CTRL || rtmpHdr->msgType == WSC_OPCODE_UPNP_MGMT)
++	{
++		DBGPRINTF(RT_DBG_INFO, "Receive a UPnP Ctrl/Mgmt Msg!\n");
++		return 0;
++	}
++	wsc_hexdump("wscCPPutMessage-K2UMsg", msg, msgLen);
++
++	len = ILibBase64Encode((unsigned char *)(msg + sizeof(RTMP_WSC_MSG_HDR)), rtmpHdr->msgLen, &encodeStr);
++	if (len >0)
++	{
++		WscUPnPCPSendAction(rtmpHdr->ipAddr, "PutMessage", &wscCPStateVarParam, &encodeStr, 1);
++		free(encodeStr);
++	}
++	return 0;
++}
++
++
++
++/********************************************************************************
++ * WscUPnPCPSendAction
++ *
++ * Description: 
++ *       Send an Action request to the specified service of a device.
++ *
++ * Parameters:
++ *   devnum -- The number of the device (order in the list,
++ *             starting with 1)
++ *   actionname -- The name of the action.
++ *   param_name -- An array of parameter names
++ *   param_val -- The corresponding parameter values
++ *   param_count -- The number of parameters
++ *
++ ********************************************************************************/
++#define CP_RESP_SUPPORT_LIST_NUM	2
++
++static char *stateVarNames[CP_RESP_SUPPORT_LIST_NUM]={"NewDeviceInfo", "NewOutMessage"};
++static char *actionResNames[CP_RESP_SUPPORT_LIST_NUM]= {"u:GetDeviceInfoResponse", "u:PutMessageResponse"};
++
++int WscUPnPCPHandleActionResponse(
++	IN struct Upnp_Action_Complete *a_event)
++{
++	IXML_NodeList *nodeList = NULL, *msgNodeList = NULL;
++	IXML_Node *element, *child = NULL;
++	char *varName = NULL;
++	struct upnpDeviceNode *nodePtr;
++	
++	char *inStr = NULL, *pWscU2KMsg = NULL;
++	unsigned char *decodeStr = NULL;
++	int i, decodeLen = 0, wscU2KMsgLen;
++	
++	unsigned int UPnPDevIP = 0;
++			
++	DBGPRINTF(RT_DBG_INFO, "ErrCode = %d, CtrlUrl=%s!\n", a_event->ErrCode, a_event->CtrlUrl);
++	
++	if(a_event->ActionResult == NULL || a_event->ErrCode != 0)
++		return 0;
++
++	// Check if this device is already in the list
++	nodePtr = WscDeviceList;
++	while (nodePtr)
++	{
++		if(strcmp(nodePtr->device.services.ControlURL, a_event->CtrlUrl) == 0)
++		{
++			UPnPDevIP = nodePtr->device.ipAddr;
++			nodePtr->device.timeCount = nodePtr->device.AdvrTimeOut;
++			break;
++		}
++		nodePtr = nodePtr->next;
++    }
++	if (UPnPDevIP == 0)
++		goto done;
++	DBGPRINTF(RT_DBG_INFO, "Find the ActionResponse Device IP=%x\n", UPnPDevIP);
++
++	
++	/*
++		We just support following ActionResponse from remote device.
++	*/
++	for (i=0; i < CP_RESP_SUPPORT_LIST_NUM; i++)
++	{
++		DBGPRINTF(RT_DBG_INFO, "check actionResNames[%d]=%s!\n", i, actionResNames[i]);
++		nodeList = ixmlDocument_getElementsByTagName(a_event->ActionResult, actionResNames[i]);
++		if(nodeList){
++			varName = stateVarNames[i];
++			break;
++		}
++	}
++
++	if(nodeList == NULL)
++	{
++		DBGPRINTF(RT_DBG_INFO, "UnSupportted ActResponse!\n");
++		goto done;
++	}
++	
++	if ((element = ixmlNodeList_item(nodeList, 0)))
++	{
++		//First check if we have supportted State Variable name!
++		ixmlNode_getElementsByTagName(element, varName, &msgNodeList);
++		if(msgNodeList != NULL)
++		{
++			DBGPRINTF(RT_DBG_INFO, "find stateVarName=%s!\n", varName);
++			while((child = ixmlNode_getFirstChild(element))!= NULL)
++			{	// Find the Response text content!
++				if (ixmlNode_getNodeType(child) == eTEXT_NODE)
++				{
++					inStr = strdup(ixmlNode_getNodeValue(child));
++					break;
++				}
++				element = child;
++			}
++			ixmlNodeList_free(msgNodeList);
++		}
++	}
++	
++	// Here depends on the ActionRequest and ActionResponse, dispatch to correct handler!
++	if(inStr!= NULL)
++	{
++		DBGPRINTF(RT_DBG_INFO, "Receive a %s Message!\n", actionResNames[i]);
++		DBGPRINTF(RT_DBG_INFO, "\tinStr=%s!\n", inStr);
++		decodeLen = ILibBase64Decode((unsigned char *)inStr, strlen(inStr), &decodeStr);
++		if((decodeLen > 0) && (ioctl_sock >= 0))
++		{
++			RTMP_WSC_U2KMSG_HDR *msgHdr;
++			WscEnvelope *msgEnvelope;
++			int msgQIdx = -1;
++			 
++			/* Prepare the msg buffers */
++			wscU2KMsgLen = wscU2KMsgCreate(&pWscU2KMsg, (char *)decodeStr, decodeLen, EAP_FRAME_TYPE_WSC);
++			if (wscU2KMsgLen == 0)
++				goto done;
++
++			/* Prepare the msg envelope */
++			if ((msgEnvelope = wscEnvelopeCreate()) == NULL)
++				goto done;
++			msgEnvelope->callBack = wscCPPutMessage;
++			
++			/* Lock the msgQ and check if we can get a valid mailbox to insert our request! */
++			if (wscMsgQInsert(msgEnvelope, &msgQIdx) != WSC_SYS_SUCCESS)
++				goto done;
++
++			// Fill the session ID to the U2KMsg buffer header.
++			msgHdr = (RTMP_WSC_U2KMSG_HDR *)pWscU2KMsg;
++			msgHdr->envID = msgEnvelope->envID;
++
++			// copy the Addr1 & Addr2
++			memcpy(msgHdr->Addr1, HostMacAddr, MAC_ADDR_LEN);
++			memcpy(msgHdr->Addr2, &UPnPDevIP, sizeof(unsigned int));
++
++			// Now send the msg to kernel space.
++			DBGPRINTF(RT_DBG_INFO, "(%s):Ready to send pWscU2KMsg(len=%d) to kernel by ioctl(%d)!\n", __FUNCTION__, wscU2KMsgLen, ioctl_sock);
++			wsc_hexdump("U2KMsg", pWscU2KMsg, wscU2KMsgLen);
++	
++			if(wsc_set_oid(RT_OID_WSC_EAPMSG, pWscU2KMsg, wscU2KMsgLen) != 0)
++				wscEnvelopeRemove(msgEnvelope, msgQIdx);
++		}
++	}
++
++
++done:
++	if (nodeList)
++		ixmlNodeList_free(nodeList);
++	if (inStr)
++		free(inStr);
++	if (pWscU2KMsg)
++		free(pWscU2KMsg);
++	if (decodeStr)
++		free(decodeStr);
++			
++	//wsc_printEvent(UPNP_CONTROL_ACTION_COMPLETE, (void *)a_event);
++	return 0;
++}
++
++
++/********************************************************************************
++ * WscUPnPCPHandleEvent
++ *
++ * Description: 
++ *       Handle a UPnP event that was received.  Process the event and update
++ *       the appropriate service state table.
++ *
++ * Parameters:
++ *   sid -- The subscription id for the event
++ *   eventkey -- The eventkey number for the event
++ *   changes -- The DOM document representing the changes
++ *
++ * Return:
++ *   None 
++ ********************************************************************************/
++void WscUPnPCPHandleEvent(
++	IN Upnp_SID sid,
++	IN int evntkey,
++	IN IXML_Document * changes)
++{
++	struct upnpDeviceNode *devNode;
++	char *inStr = NULL, *pWscU2KMsg = NULL;
++	unsigned char *decodeStr = NULL;
++	int decodeLen, wscU2KMsgLen;
++	unsigned int UPnPDevIP = 0;
++	
++	devNode = WscDeviceList;
++	while (devNode) 
++	{
++		if(strcmp(devNode->device.services.SID, sid) == 0) 
++		{
++			devNode->device.timeCount = devNode->device.AdvrTimeOut;
++			UPnPDevIP = devNode->device.ipAddr;
++			
++			DBGPRINTF(RT_DBG_INFO, "Received WscService Event: %d for SID %s\n", evntkey, sid);
++			WscStateVarUpdate(devNode, changes);
++
++			inStr = devNode->device.services.StateVarVal[WSC_EVENT_WLANEVENT];
++			if (inStr)
++			{
++#define WSC_EVENT_WLANEVENT_MSG_LEN_MIN		18	// The first byte is the msg type, the next 17 bytes is the MAC address in xx:xx format
++
++				DBGPRINTF(RT_DBG_INFO, "\tWLANEvent=%s!\n", devNode->device.services.StateVarVal[WSC_EVENT_WLANEVENT]);
++				decodeLen = ILibBase64Decode((unsigned char *)inStr, strlen(inStr), &decodeStr);
++
++				if((decodeLen > WSC_EVENT_WLANEVENT_MSG_LEN_MIN) && (ioctl_sock >= 0))
++				{
++					RTMP_WSC_U2KMSG_HDR *msgHdr;
++					WscEnvelope *msgEnvelope;
++					int msgQIdx = -1;
++			 
++					
++			 		decodeLen -= WSC_EVENT_WLANEVENT_MSG_LEN_MIN;
++					
++					/* Prepare the msg buffers */
++					wscU2KMsgLen = wscU2KMsgCreate(&pWscU2KMsg, (char *)(decodeStr + WSC_EVENT_WLANEVENT_MSG_LEN_MIN), 
++													decodeLen, EAP_FRAME_TYPE_WSC);
++					if (wscU2KMsgLen == 0)
++						goto done;
++
++					/* Prepare the msg envelope */
++					if ((msgEnvelope = wscEnvelopeCreate()) == NULL)
++						goto done;
++					msgEnvelope->callBack = wscCPPutMessage;
++			
++					/* Lock the msgQ and check if we can get a valid mailbox to insert our request! */
++					if (wscMsgQInsert(msgEnvelope, &msgQIdx) != WSC_SYS_SUCCESS)
++						goto done;
++
++					// Fill the session ID to the U2KMsg buffer header.
++					msgHdr = (RTMP_WSC_U2KMSG_HDR *)pWscU2KMsg;
++					msgHdr->envID = msgEnvelope->envID;
++
++					// copy the Addr1 & Addr2
++					memcpy(msgHdr->Addr1, HostMacAddr, MAC_ADDR_LEN);
++					memcpy(msgHdr->Addr2, &UPnPDevIP, sizeof(unsigned int));
++
++					// Now send the msg to kernel space.
++					DBGPRINTF(RT_DBG_INFO, "(%s):Ready to send pWscU2KMsg(len=%d) to kernel by ioctl(%d)!\n", __FUNCTION__, wscU2KMsgLen, ioctl_sock);
++					wsc_hexdump("U2KMsg", pWscU2KMsg, wscU2KMsgLen);
++	
++					if(wsc_set_oid(RT_OID_WSC_EAPMSG, pWscU2KMsg, wscU2KMsgLen) != 0)
++						wscEnvelopeRemove(msgEnvelope, msgQIdx);
++				}
++			}
++			if (devNode->device.services.StateVarVal[WSC_EVENT_APSTATUS] != NULL)
++				DBGPRINTF(RT_DBG_INFO, "\tAPStatus=%s!\n", devNode->device.services.StateVarVal[WSC_EVENT_APSTATUS]);
++			if (devNode->device.services.StateVarVal[WSC_EVENT_STASTATUS] != NULL)
++				DBGPRINTF(RT_DBG_INFO, "\tSTAStatus=%s!\n", devNode->device.services.StateVarVal[WSC_EVENT_STASTATUS]);
++			break;
++		}
++		devNode = devNode->next;
++	}
++	
++done:
++
++}
++
++
++/********************************************************************************
++ * WscUPnPCPHandleSubscribeUpdate
++ *
++ * Description: 
++ *       Handle a UPnP subscription update that was received.  Find the 
++ *       service the update belongs to, and update its subscription
++ *       timeout.
++ *
++ * Parameters:
++ *   eventURL -- The event URL for the subscription
++ *   sid -- The subscription id for the subscription
++ *   timeout  -- The new timeout for the subscription
++ *
++ * Return:
++ *    None
++ ********************************************************************************/
++void WscUPnPCPHandleSubscribeUpdate(
++	IN char *eventURL,
++	IN Upnp_SID sid,
++	IN int timeout)
++{
++	struct upnpDeviceNode *tmpdevnode;
++
++	tmpdevnode = WscDeviceList;
++	while (tmpdevnode) 
++	{
++		if( strcmp(tmpdevnode->device.services.EventURL, eventURL) == 0) 
++		{
++			DBGPRINTF(RT_DBG_INFO, "Received WscService Event Renewal for eventURL %s\n", eventURL);
++			strcpy(tmpdevnode->device.services.SID, sid);
++			break;
++		}
++
++		tmpdevnode = tmpdevnode->next;
++	}
++}
++
++
++
++/********************************************************************************
++ * WscUPnPCPDeviceHandler
++ *
++ * Description: 
++ *       The callback handler registered with the SDK while registering the Control Point.
++ *       This callback funtion detects the type of callback, and passes the request on to 
++ *       the appropriate function.
++ *
++ * Parameters:
++ *   EventType -- The type of callback event
++ *   Event -- Data structure containing event data
++ *   Cookie -- Optional data specified during callback registration
++ *
++ * Return:
++ *    Always zero 
++ ********************************************************************************/
++int
++WscUPnPCPDeviceHandler(
++	IN Upnp_EventType EventType,
++	IN void *Event,
++	IN void *Cookie)
++{
++    //wsc_PrintEvent( EventType, Event );
++
++	switch (EventType) 
++	{
++		/*
++			SSDP Stuff 
++		*/
++		case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
++		case UPNP_DISCOVERY_SEARCH_RESULT:
++			{
++				struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event;
++				unsigned int ipAddr = 0;
++				if (d_event->ErrCode != UPNP_E_SUCCESS)
++				{
++					DBGPRINTF(RT_DBG_ERROR, "Error in Discovery Adv Callback -- %d\n", d_event->ErrCode);
++				}
++				else 
++				{
++					if (strcmp(d_event->DeviceType, WscDeviceTypeStr) == 0)
++					{	//We just need to take care about the WscDeviceTypeStr
++						DBGPRINTF(RT_DBG_INFO, "Receive a Advertisement from a WFADevice(URL=%s)\n", d_event->Location);
++						if (WscUPnPCPAddDevice(WscDeviceTypeStr, d_event, &ipAddr) == WSC_SYS_SUCCESS)
++						{
++							WscUPnPCPDumpList(); // Printing the Wsc Device List for debug
++							WscUPnPCPSendAction(ipAddr,"GetDeviceInfo", NULL, NULL, 0);
++						}
++					}
++				}
++				break;
++			}
++
++		case UPNP_DISCOVERY_SEARCH_TIMEOUT:
++			// Nothing to do here..
++			break;
++
++		case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
++			{
++				struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event;
++
++				if (d_event->ErrCode != UPNP_E_SUCCESS)
++				{
++					DBGPRINTF(RT_DBG_ERROR, "Error in Discovery ByeBye Callback -- %d\n", d_event->ErrCode);
++				}
++				else
++				{
++					DBGPRINTF(RT_DBG_INFO, "Received ByeBye for Device: %s\n", d_event->DeviceId);
++	                WscUPnPCPRemoveDevice(d_event->DeviceId);
++					//Dump it for debug
++					WscUPnPCPDumpList();
++				}
++
++				break;
++			}
++
++		/*
++			SOAP Stuff 
++		*/
++		case UPNP_CONTROL_ACTION_COMPLETE:
++			{
++				struct Upnp_Action_Complete *a_event = (struct Upnp_Action_Complete *)Event;
++
++				if (a_event->ErrCode != UPNP_E_SUCCESS)
++					DBGPRINTF(RT_DBG_ERROR, "Error in  Action Complete Callback -- %d\n", a_event->ErrCode);
++				else 
++				{
++					DBGPRINTF(RT_DBG_INFO, "Get event:UPNP_CONTROL_ACTION_COMPLETE!\n");
++					WscUPnPCPHandleActionResponse(a_event);
++				}
++				break;
++			}
++
++		case UPNP_CONTROL_GET_VAR_COMPLETE:
++			{
++				struct Upnp_State_Var_Complete *sv_event = (struct Upnp_State_Var_Complete *)Event;
++
++				if (sv_event->ErrCode != UPNP_E_SUCCESS)
++				{
++					DBGPRINTF(RT_DBG_ERROR, "Error in Get Var Complete Callback -- %d\n", sv_event->ErrCode);
++                } else {
++					WscUPnPCPHandleGetVar(sv_event->CtrlUrl, sv_event->StateVarName, sv_event->CurrentVal);
++				}
++
++				break;
++			}
++
++		/*
++			GENA Stuff 
++		*/
++		case UPNP_EVENT_RECEIVED:
++			{
++				struct Upnp_Event *e_event = (struct Upnp_Event *)Event;
++				
++				DBGPRINTF(RT_DBG_INFO, "Get event:UPNP_EVENT_RECEIVED!\n");
++				
++				WscUPnPCPHandleEvent(e_event->Sid, e_event->EventKey, e_event->ChangedVariables);
++				break;
++			}
++
++		case UPNP_EVENT_SUBSCRIBE_COMPLETE:
++		case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
++		case UPNP_EVENT_RENEWAL_COMPLETE:
++			{
++				struct Upnp_Event_Subscribe *es_event = (struct Upnp_Event_Subscribe *)Event;
++
++				if (es_event->ErrCode != UPNP_E_SUCCESS) 
++				{
++					DBGPRINTF(RT_DBG_ERROR, "Error in Event Subscribe Callback -- %d\n", es_event->ErrCode);
++				} else {
++					WscUPnPCPHandleSubscribeUpdate(es_event->PublisherUrl, es_event->Sid, es_event->TimeOut);
++                }
++
++				break;
++			}
++
++		case UPNP_EVENT_AUTORENEWAL_FAILED:
++		case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
++			{
++				int TimeOut = DEF_SUBSCRIPTION_TIMEOUT;
++				Upnp_SID newSID;
++				int ret;
++
++				struct Upnp_Event_Subscribe *es_event = (struct Upnp_Event_Subscribe *)Event;
++
++				ret = UpnpSubscribe(WscCPHandle, es_event->PublisherUrl, &TimeOut, newSID);
++
++				if (ret == UPNP_E_SUCCESS) 
++				{
++					DBGPRINTF(RT_DBG_INFO, "Subscribed to EventURL with SID=%s\n", newSID);
++					WscUPnPCPHandleSubscribeUpdate(es_event->PublisherUrl, newSID, TimeOut);
++				} else {
++					DBGPRINTF(RT_DBG_ERROR, "Error Subscribing to EventURL -- %d\n", ret);
++				}
++				break;
++			}
++
++			/*
++				Ignore these cases, since this is not a device 
++			*/
++		case UPNP_EVENT_SUBSCRIPTION_REQUEST:
++		case UPNP_CONTROL_GET_VAR_REQUEST:
++		case UPNP_CONTROL_ACTION_REQUEST:
++			break;
++	}
++
++	return 0;
++}
++
++
++/********************************************************************************
++ * WscUPnPCPGetVar
++ *
++ * Description: 
++ *       Send a GetVar request to the specified service of a device.
++ *
++ * Parameters:
++ *   ipAddr -- The IP address of the device.
++ *   varname -- The name of the variable to request.
++ *
++ * Return:
++ *    UPNP_E_SUCCESS - if success
++ *    WSC_SYS_ERROR  - if failure
++ ********************************************************************************/
++int
++WscUPnPCPGetVar(
++	IN uint32 ipAddr,
++	IN char *varname)
++{
++	struct upnpDeviceNode *devnode;
++	int rc;
++
++	rc = WscUPnPCPGetDevice(ipAddr, &devnode);
++
++	if (rc == WSC_SYS_SUCCESS)
++	{
++		rc = UpnpGetServiceVarStatusAsync( WscCPHandle, devnode->device.services.ControlURL,
++                                           varname, WscUPnPCPDeviceHandler, NULL);
++		if( rc != UPNP_E_SUCCESS )
++		{
++			DBGPRINTF(RT_DBG_ERROR, "Error in UpnpGetServiceVarStatusAsync -- %d\n", rc);
++			rc = WSC_SYS_ERROR;
++		}
++	}
++
++	return rc;
++}
++
++
++#if 0
++int WscUPnPCPGetVarSampleCode(
++	IN int devnum)
++{
++    return WscUPnPCPGetVar(devnum, "Power");
++}
++#endif
++
++
++/********************************************************************************
++ * WscUPnPCPVerifyTimeouts
++ *
++ * Description: 
++ *       Checks the advertisement each device in the global device list. 
++ *		 If an advertisement expires, the device is removed from the list.
++ *		 If an advertisement is about to expire, a search request is sent 
++ *		 for that device.  
++ *
++ * Parameters:
++ *    incr -- The increment to subtract from the timeouts each time the
++ *            function is called.
++ *
++ * Return:
++ *    None
++ ********************************************************************************/
++void WscUPnPCPVerifyTimeouts(int incr)
++{
++	struct upnpDeviceNode *prevdevnode, *curdevnode;
++	int ret, timeLeft;
++
++	prevdevnode = NULL;
++	curdevnode = WscDeviceList;
++
++	while (curdevnode)
++	{
++		curdevnode->device.timeCount += incr;
++		timeLeft = curdevnode->device.AdvrTimeOut - curdevnode->device.timeCount;
++		
++		if (timeLeft <= 0)
++		{
++			/* This advertisement has expired, so we should remove the device from the list */
++			if (WscDeviceList == curdevnode)
++				WscDeviceList = curdevnode->next;
++			else
++				prevdevnode->next = curdevnode->next;
++			DBGPRINTF(RT_DBG_INFO, "%s(), delete the node(ipAddr=0x%08x)!\n", __FUNCTION__, curdevnode->device.ipAddr);
++			WscUPnPCPDeleteNode(curdevnode);
++			curdevnode = prevdevnode ? prevdevnode->next : WscDeviceList;
++		} else {
++			if (timeLeft < 3 * incr)
++			{
++				/*
++					This advertisement is about to expire, so send out a search request for this 
++					device UDN to try to renew
++				*/
++				ret = UpnpSearchAsync(WscCPHandle, incr, curdevnode->device.UDN, NULL);
++				if (ret != UPNP_E_SUCCESS)
++					DBGPRINTF(RT_DBG_ERROR, "Err sending SearchReq for Device UDN: %s -- err = %d\n", curdevnode->device.UDN, ret);
++			}
++
++			prevdevnode = curdevnode;
++			curdevnode = curdevnode->next;
++		}
++
++	}
++}
++
++
++/********************************************************************************
++ * WscUPnPCPHouseKeep
++ *
++ * Description: 
++ *       Function that runs in its own thread and monitors advertisement
++ *       and subscription timeouts for devices in the global device list.
++ *
++ * Parameters:
++ *    None
++ *  
++ * Return:
++ *    UPNP_E_SUCCESS - if success
++ *    WSC_SYS_ERROR  - if failure
++ ********************************************************************************/
++void *WscUPnPCPHouseKeep(void *args)
++{
++	int incr = 30;              // how often to verify the timeouts, in seconds
++
++	while(1)
++	{
++		sleep(incr);
++		WscUPnPCPVerifyTimeouts(incr);
++	}
++}
++
++
++/********************************************************************************
++ * WscUPnPCPRefresh
++ *
++ * Description: 
++ *       Clear the current wsc device list and issue new search
++ *	 requests to build it up again from scratch.
++ *
++ * Parameters:
++ *   None
++ *
++ * Return:
++ *    Always success
++ ********************************************************************************/
++int WscUPnPCPRefresh(void)
++{
++	int rc;
++
++	WscUPnPCPRemoveAll();
++
++	/*
++		Search for all devices of type Wsc UPnP Device version 1, 
++		waiting for up to 5 seconds for the response 
++	*/
++	rc = UpnpSearchAsync(WscCPHandle, 5, WscDeviceTypeStr, NULL);
++	if (rc != UPNP_E_SUCCESS)
++	{
++		DBGPRINTF(RT_DBG_ERROR, "Error sending search request%d\n", rc);
++		return WSC_SYS_ERROR;
++	}
++
++	return WSC_SYS_SUCCESS;
++}
++
++
++/******************************************************************************
++ * WscUPnPCPStop
++ *
++ * Description: 
++ *      Stop the UPnP Control Point, and remove all remote Device node.
++ *
++ * Parameters:
++ *		void
++ *   
++ * Return:
++ *    Always success
++ *****************************************************************************/
++int WscUPnPCPStop(void)
++{
++	WscUPnPCPRemoveAll();
++    UpnpUnRegisterClient(WscCPHandle);
++
++    return WSC_SYS_SUCCESS;
++}
++
++
++/******************************************************************************
++ * WscUPnPCPStart
++ *
++ * Description: 
++ *      Registers the UPnP Control Point, and sends out M-SEARCH.
++ *
++ * Parameters:
++ *
++ *   char *ipAddr 		 - ip address to initialize the Control Point Service.
++ *   unsigned short port - port number to initialize the control Point Service.
++ *   
++ * Return:
++ *    success - WSC_SYS_SUCCESS
++ *    failed  - WSC_SYS_ERROR
++ *****************************************************************************/
++int WscUPnPCPStart(
++	IN char *ipAddr,
++	IN unsigned short port)
++{
++	int rc;
++
++	DBGPRINTF(RT_DBG_INFO, "Registering Control Point...\n");
++	rc = UpnpRegisterClient(WscUPnPCPDeviceHandler, &WscCPHandle, &WscCPHandle);
++	if (rc != UPNP_E_SUCCESS)
++	{
++		DBGPRINTF(RT_DBG_ERROR, "Error registering CP: %d\n", rc);
++		
++		return WSC_SYS_ERROR;
++	}
++	DBGPRINTF(RT_DBG_INFO, "Control Point Registered\n");
++
++	WscUPnPCPRefresh();
++
++	// start a timer thread
++	if(rc == 0)
++	
++	return WSC_SYS_SUCCESS;
++
++}
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_upnp_cp.h
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_upnp_cp.h
+@@ -0,0 +1,9 @@
++/*
++	wsc upnp control point related structures
++
++*/
++#ifndef __WSC_UPNP_CP_H__
++#define __WSC_UPNP_CP_H__
++
++
++#endif
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_upnp_device.c
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_upnp_device.c
+@@ -0,0 +1,2225 @@
++///////////////////////////////////////////////////////////////////////////
++//
++// Copyright (c) 2000-2003 Mediatek Corporation 
++// All rights reserved. 
++//
++// Redistribution and use in source and binary forms, with or without 
++// modification, are permitted provided that the following conditions are met: 
++//
++// * Redistributions of source code must retain the above copyright notice, 
++// this list of conditions and the following disclaimer. 
++// * Redistributions in binary form must reproduce the above copyright notice, 
++// this list of conditions and the following disclaimer in the documentation 
++// and/or other materials provided with the distribution. 
++// * Neither name of Intel Corporation nor the names of its contributors 
++// may be used to endorse or promote products derived from this software 
++// without specific prior written permission.
++// 
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 
++// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
++// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
++// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
++// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
++// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++//
++///////////////////////////////////////////////////////////////////////////
++
++#include <stdio.h>
++#include <signal.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include "upnphttp.h"
++#include "upnpevents.h"
++#include "upnpglobalvars.h"
++#include "wsc_msg.h"
++#include "wsc_common.h"
++#include "wsc_ioctl.h"
++#include "wsc_upnp.h"
++#include "wsc_upnp_device.h"
++
++#include "upnpreplyparse.h"/* for struct NameValueParserData */
++
++
++//#include "sample_util.h"
++//#include "ThreadPool.h"
++//#include "service_table.h"
++//#include "upnpapi.h"
++//#include "ssdplib.h"
++
++#if 0 //Mediatek :  move to upnphttp.h
++#define DO_NOTHING_IN_MAIN_LOOP	3
++#endif
++
++#define UUID_STR_LEN            36	
++#define WSC_UPNP_UUID_STR_LEN	(5 + UUID_STR_LEN + 1)	// The UUID string get from the driver by ioctl and the strlen is 36 plus 1 '\0', 
++						        // and we need extra 5 bytes for prefix string "uuid:"
++
++struct upnpCPNode *WscCPList = NULL;
++
++int wscDevHandle = -1;	// Device handle of "wscLocalDevice" supplied by UPnP SDK.
++
++/* The amount of time (in seconds) before advertisements will expire */
++int defAdvrExpires = 100;
++
++
++/* 
++	Structure for storing Wsc Device Service identifiers and state table 
++*/
++#define WSC_ACTION_COUNTS		13
++#define WSC_ACTION_MAXCOUNT		WSC_ACTION_COUNTS
++
++typedef int (*upnp_action) (IXML_Document *request, uint32 ipAddr, IXML_Document **out, char **errorString);
++
++struct WscDevActionList {
++  char *actionNames[WSC_ACTION_COUNTS];
++  upnp_action actionFuncs[WSC_ACTION_COUNTS];
++};
++
++struct WscDevActionList wscDevActTable;
++
++
++//static char *wscStateVarName[] = {"WLANEvent", "APStatus", "STAStatus"};
++static char wscStateVarCont[WSC_STATE_VAR_COUNT][WSC_STATE_VAR_MAX_STR_LEN];
++
++static char *wscStateVarDefVal[] = {"", "0", "0"};
++
++char wscAckMsg[]= {0x10, 0x4a, 0x00, 0x01, 0x10, 0x10, 0x22, 0x00, 0x01, 0x0d, 0x10, 0x1a, 0x00, 0x10, 0x00, 0x00,
++				   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x39, 
++				   0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
++				   0x00, 0x00};
++
++
++typedef enum _WSC_EVENT_STATUS_CODE{
++	WSC_EVENT_STATUS_CONFIG_CHANGED = 0x00000001,
++	WSC_EVENT_STATUS_AUTH_THRESHOLD_REACHED = 0x00000010,
++}WSC_EVENT_STATUS_CODE;
++
++typedef enum _WSC_EVENT_WLANEVENTTYPE_CODE{
++	WSC_EVENT_WLANEVENTTYPE_PROBE = 1,
++	WSC_EVENT_WLANEVENTTYPE_EAP = 2,
++}WSC_EVENT_WLANEVENTTYPE_CODE;
++
++
++int senderID = 1;
++char CfgMacStr[18]={0};
++
++#ifdef ENABLE_WSC_SERVICE
++/************************************************************************
++* Function : addToAction											
++*																	
++* Parameters:														
++*	IN int response: flag to tell if the ActionDoc is for response 
++*					or request
++*	INOUT IXML_Document **ActionDoc: request or response document
++*	IN char *ActionName: Name of the action request or response
++*	IN char *ServType: Service type
++*	IN char * ArgName: Name of the argument
++*	IN char * ArgValue: Value of the argument
++*
++* Description:		
++*	This function adds the argument in the action request or response. 
++* This function creates the action request or response if it is a first
++* argument else it will add the argument in the document
++*
++* Returns: int
++*	returns 0 if successful else returns appropriate error
++***************************************************************************/
++int
++addToAction( IN int response,
++             INOUT IXML_Document * ActionDoc,
++             IN const char *ActionName,
++             IN const char *ServType,
++             IN const char *ArgName,
++             IN const char *ArgValue )
++{
++    int rc = 0;
++	int buffer_size = 2000;
++
++    if( ActionName == NULL || ServType == NULL )
++	{
++        return -1;
++    }
++
++	*ActionDoc = malloc(buffer_size);
++	
++	if (*ActionDoc == NULL)
++	{
++		printf("malloc failed in addToAction()\n");
++        return -1;
++	}
++
++	memset(*ActionDoc, 0x00, buffer_size);
++	
++    if(response)
++	{
++		if (ArgName != NULL)
++		{
++		        sprintf( *ActionDoc,
++		                 "<u:%sResponse xmlns:u=\"%s\"><%s>%s</%s></u:%sResponse>",
++		                 ActionName, ServType, ArgName, ArgValue, ArgName, ActionName);
++		}
++		else
++		{
++		        sprintf( *ActionDoc,
++		                 "<u:%sResponse xmlns:u=\"%s\"></u:%sResponse>",
++		                 ActionName, ServType, ActionName);
++		}
++    } 
++	else
++	{
++        sprintf( *ActionDoc, "<u:%s xmlns:u=\"%s\"></u:%s>",
++                 ActionName, ServType, ActionName );
++    }
++    return rc;
++}
++
++/************************************************************************
++* Function : UpnpAddToActionResponse									
++*																	
++* Parameters:
++*	INOUT IXML_Document **ActionResponse: action response document	
++*	IN char * ActionName: Name of the action request or response
++*	IN char * ServType: Service type
++*	IN int ArgName :Name of argument to be added in the action response
++*	IN char * ArgValue : value of the argument
++*
++* Description:		
++*	This function adds the argument in the action response. Its a wrapper 
++* function that calls addToAction function to add the argument in the 
++* action response.
++*
++* Returns: int
++*	returns 0 if successful 
++*	else returns appropriate error
++***************************************************************************/
++int
++UpnpAddToActionResponse( INOUT IXML_Document * ActionResponse,
++                         IN const char *ActionName,
++                         IN const char *ServType,
++                         IN const char *ArgName,
++                         IN const char *ArgValue )
++{
++    return addToAction( 1, ActionResponse, ActionName, ServType, ArgName,
++                        ArgValue );
++}
++
++/************************************************************************
++* Function : UpnpAddToAction									
++*																	
++* Parameters:
++*	INOUT IXML_Document **ActionDoc: action request document	
++*	IN char * ActionName: Name of the action request or response
++*	IN char * ServType: Service type
++*	IN int ArgName :Name of argument to be added in the action response
++*	IN char * ArgValue : value of the argument
++*
++* Description:		
++*	This function adds the argument in the action request. Its a wrapper 
++* function that calls addToAction function to add the argument in the 
++* action request.
++*
++* Returns: int
++*	returns 0 if successful 
++*	else returns appropriate error
++***************************************************************************/
++int
++UpnpAddToAction( INOUT IXML_Document * ActionDoc,
++                 const char *ActionName,
++                 const char *ServType,
++                 const char *ArgName,
++                 const char *ArgValue )
++{
++
++    return addToAction( 0, ActionDoc, ActionName, ServType, ArgName,
++                        ArgValue );
++}
++#endif /* ENABLE_WSC_SERVICE */
++
++
++/******************************************************************************
++ * wscU2KMsgCreate
++ *
++ * Description: 
++ *       Allocate the memory and copy the content to the buffer.
++ *
++ * Parameters:
++ *    char **dstPtr - pointer used for refer the allocated U2Kmsg.
++ *    char *srcPtr  - the message need to copy into the U2KMsg.
++ *    int 	msgLen  - length of the message "srcPtr".
++ *    int	EAPType - the EAP message type. 
++ *    				 			1=Identity, 0xFE=reserved, used by WSC
++ * Return Value:
++ *    Total length of created U2KMsg
++ *    	zero 	- if failure
++ *    	others  - if success 
++ *****************************************************************************/
++int 
++wscU2KMsgCreate(
++	INOUT char **dstPtr,
++	IN char *srcPtr,
++	IN int msgLen,
++	IN int EAPType)
++{
++	int totalLen;
++	char *pPos = NULL, *pMsgPtr = NULL;
++	RTMP_WSC_U2KMSG_HDR *pU2KMsgHdr; 
++	IEEE8021X_FRAME *p1xFrameHdr;
++	EAP_FRAME	*pEAPFrameHdr;
++		
++	/* Allocate the msg buffer and fill the content */
++	totalLen = sizeof(RTMP_WSC_U2KMSG_HDR) + msgLen;
++	if ((pMsgPtr = malloc(totalLen)) != NULL)
++	{
++		memset(pMsgPtr , 0, totalLen);
++		pU2KMsgHdr = (RTMP_WSC_U2KMSG_HDR *)pMsgPtr;
++		
++		// create the IEEE8021X_FRAME header
++		p1xFrameHdr = &pU2KMsgHdr->IEEE8021XHdr;
++		p1xFrameHdr->Version = IEEE8021X_FRAME_VERSION;
++		p1xFrameHdr->Length = htons(sizeof(EAP_FRAME) + msgLen);
++		p1xFrameHdr->Type = IEEE8021X_FRAME_TYPE_EAP;
++
++		// create the EAP header
++		pEAPFrameHdr = &pU2KMsgHdr->EAPHdr;
++		pEAPFrameHdr->Code = EAP_FRAME_CODE_RESPONSE;
++		pEAPFrameHdr->Id = 1;  // The Id field is useless here.
++		pEAPFrameHdr->Type = EAPType;
++		pEAPFrameHdr->Length = htons(sizeof(EAP_FRAME) + msgLen);
++
++		//copy the msg payload
++		pPos = (char *)(pMsgPtr + sizeof(RTMP_WSC_U2KMSG_HDR));
++		memcpy(pPos, srcPtr, msgLen);
++		*dstPtr = pMsgPtr;
++		DBGPRINTF(RT_DBG_INFO, "create U2KMsg success!MsgLen = %d, headerLen=%d! totalLen=%d!\n", msgLen, sizeof(RTMP_WSC_U2KMSG_HDR), totalLen);
++	} else {
++		DBGPRINTF(RT_DBG_INFO, "malloc allocation(size=%d) failed in wscU2KMsgCreate()!\n", totalLen);
++		totalLen = 0;
++	}
++
++	return totalLen;
++}
++
++
++/*---------------------------action handler---------------------------*/
++
++/******************************************************************************
++ * WscDevGetAPSettings
++ *
++ * Description: 
++ *       Wsc Service action callback used to get the AP's settings.
++ *
++ * Parameters:
++ *    
++ *    IXML_Document * in -  action request document
++ *    IXML_Document **out - action result document
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *  
++ * Return:
++ *    0 		- if success
++ *    others 				- if failure
++ *****************************************************************************/
++int
++WscDevGetAPSettings(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++    IXML_Document * out,
++    OUT char **errorString )
++{
++	// TODO: Need to complete it, currently do nothing and return directly.
++	
++	//create a response	
++	if(UpnpAddToActionResponse(out, "GetAPSettings", WscServiceTypeStr, NULL, NULL) != 0)
++	{
++		(*out) = NULL;
++		(*errorString) = "Internal Error";
++		
++		return -1;
++	}
++	
++	return 0;
++
++}
++
++
++/******************************************************************************
++ * WscDevGetSTASettings
++ *
++ * Description: 
++ *      Wsc Service action callback used to get the STA's settings.
++ *
++ * Parameters:
++ *   
++ *    IXML_Document * in -  action request document
++ *    IXML_Document **out - action result document
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *  
++ * Return:
++ *    0 		- if success
++ *    others 				- if failure
++ *****************************************************************************/
++int
++WscDevGetSTASettings(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++	// TODO: Need to complete it, currently do nothing and return directly.
++	
++	//create a response	
++	if(UpnpAddToActionResponse(out, "GetSTASettings", WscServiceTypeStr, NULL, NULL) != 0)
++	{
++		(*out) = NULL;
++		(*errorString) = "Internal Error";
++		
++		return -1;
++	}
++	
++	return 0;
++
++}
++
++/******************************************************************************
++ * WscDevSetAPSettings
++ *
++ * Description: 
++ *       Wsc Service action callback used to set the AP's settings.
++ *
++ * Parameters:
++ *   
++ *    IXML_Document * in -  action request document
++ *    IXML_Document **out - action result document
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *  
++ * Return:
++ *    0 		- if success
++ *    others 				- if failure
++ *****************************************************************************/
++int
++WscDevSetAPSettings(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++	// TODO: Need to complete it, currently do nothing and return directly.
++	
++	//create a response	
++	if(UpnpAddToActionResponse(out, "SetAPSettings", WscServiceTypeStr, NULL, NULL) != 0)
++	{
++		(*out) = NULL;
++		(*errorString) = "Internal Error";
++		
++		return -1;
++	}
++	
++	return 0;
++
++}
++
++
++/******************************************************************************
++ * WscDevDelAPSettings
++ *
++ * Description: 
++ *       Wsc Service action callback used to delete the AP's settings.
++ *
++ * Parameters:
++ *   
++ *    IXML_Document * in -  action request document
++ *    IXML_Document **out - action result document
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *  
++ * Return:
++ *    0 		- if success
++ *    others 				- if failure
++ *****************************************************************************/
++int
++WscDevDelAPSettings(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++	// TODO: Need to complete it, currently do nothing and return directly.
++	
++	//create a response	
++	if(UpnpAddToActionResponse(out, "DelAPSettings", WscServiceTypeStr, NULL, NULL) != 0)
++	{
++		(*out) = NULL;
++		(*errorString) = "Internal Error";
++		
++		return -1;
++	}
++	
++	return 0;
++
++}
++
++
++/******************************************************************************
++ * WscDevSetSTASettings
++ *
++ * Description: 
++ *       Wsc Service action callback used to set the STA's settings.
++ *
++ * Parameters:
++ *  
++ *    IXML_Document * in -  action request document
++ *    IXML_Document **out - action result document
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *  
++ * Return:
++ *    0 		- if success
++ *    others 				- if failure
++ *****************************************************************************/
++int
++WscDevSetSTASettings(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++
++	// TODO: Need to complete it, currently do nothing and return directly.
++	
++	//create a response	
++	if(UpnpAddToActionResponse(out, "SetSTASettings", WscServiceTypeStr, NULL, NULL) != 0)
++	{
++		(*out) = NULL;
++		(*errorString) = "Internal Error";
++		
++		return -1;
++	}
++	
++	return 0;
++
++}
++
++
++/******************************************************************************
++ * WscDevRebootAP
++ *
++ * Description: 
++ *       Wsc Service action callback used to reboot the AP.
++ *
++ * Parameters:
++ *   
++ *    IXML_Document * in -  action request document
++ *    IXML_Document **out - action result document
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *  
++ * Return:
++ *    0 		- if success
++ *    others 				- if failure
++ *****************************************************************************/
++int
++WscDevRebootAP(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++	// TODO: Need to complete it, currently do nothing and return directly.
++	
++	//create a response	
++	if(UpnpAddToActionResponse(out, "RebootAP", WscServiceTypeStr, NULL, NULL) != 0)
++	{
++		(*out) = NULL;
++		(*errorString) = "Internal Error";
++		
++		return -1;
++	}
++	
++	return 0;
++
++}
++
++
++/******************************************************************************
++ * WscDevResetAP
++ *
++ * Description: 
++ *       Wsc Service action callback used to reset the AP device to factory default config.
++ *
++ * Parameters:
++ *   
++ *    IXML_Document * in -  action request document
++ *    IXML_Document **out - action result document
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *  
++ * Return:
++ *    0 		- if success
++ *    others 				- if failure
++ *****************************************************************************/
++int 
++WscDevResetAP(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++	// TODO: Need to complete it, currently do nothing and return directly.
++	
++	//create a response	
++	if(UpnpAddToActionResponse(out, "ResetAP", WscServiceTypeStr, NULL, NULL) != 0)
++	{
++		(*out) = NULL;
++		(*errorString) = "Internal Error";
++		
++		return -1;
++	}
++	
++	return 0;
++}
++
++
++/******************************************************************************
++ * WscDevRebootSTA
++ *
++ * Description: 
++ *       Wsc Service action callback used to reboot the STA.
++ *
++ * Parameters:
++ *   
++ *    IXML_Document * in -  action request document
++ *    IXML_Document **out - action result document
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *  
++ * Return:
++ *    0 		- if success
++ *    others 				- if failure
++ *****************************************************************************/
++int
++WscDevRebootSTA(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++	// TODO: Need to complete it, currently do nothing and return directly.
++	
++	//create a response	
++	if(UpnpAddToActionResponse(out, "RebootSTA", WscServiceTypeStr, NULL, NULL) != 0)
++	{
++		(*out) = NULL;
++		(*errorString) = "Internal Error";
++		
++		return -1;
++	}
++	
++	return 0;
++}
++
++
++/******************************************************************************
++ * WscDevResetSTA
++ *
++ * Description: 
++ *       Wsc Service action callback used to reset the STA to factory default value.
++ *
++ * Parameters:
++ *   
++ *    IXML_Document * in -  action request document
++ *    IXML_Document **out - action result document
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *  
++ * Return:
++ *    0 		- if success
++ *    others 				- if failure
++ *****************************************************************************/
++int
++WscDevResetSTA(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++	
++	// TODO: Need to complete it, currently do nothing and return directly.
++
++	//create a response	
++	if(UpnpAddToActionResponse(out, "ResetSTA", WscServiceTypeStr, NULL, NULL) != 0)
++	{
++		( *out ) = NULL;
++		( *errorString ) = "Internal Error";
++		
++		return -1;
++	}
++	return 0;
++
++}
++
++
++/******************************************************************************
++ * WscDevSetSelectedRegistrar
++ *
++ * Description: 
++ *       This action callback used to receive a SetSelectedRegistar message send by
++ *       contorl Point(Registrar). 
++ *
++ * Parameters:
++ *   
++ *    IXML_Document * in -  action request document
++ *    IXML_Document **out - action result document
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *  
++ * Return:
++ *    0 - if success
++ *    others - if failure
++ *****************************************************************************/
++int WscDevSetSelectedRegistrar(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++	char *inStr = NULL;
++	unsigned char *decodeStr = NULL;
++	int decodeLen, retVal = -1;
++	int inStrLen=0;	
++
++	(*out) = NULL;
++	(*errorString) = NULL;
++
++	inStr = WSCGetValueFromNameValueList((char *)(h->req_buf + h->req_contentoff), "NewMessage", &inStrLen);
++	ASSERT(inStrLen > 0);
++	if (!inStr)
++	{
++		(*errorString) = "Invalid SetSelectedRegistrar mesg parameter";
++		SoapError(h, 402, "Invalid Args");		
++		return -1;
++	}
++	decodeLen = ILibBase64Decode((unsigned char *)inStr, inStrLen, &decodeStr);
++	if (decodeLen == 0 || decodeStr == NULL)
++	{
++		goto done;
++	}
++#ifdef RT_DEBUG
++	wsc_hexdump("WscDevSetSelectedRegistrar", (char *)decodeStr, decodeLen);
++#endif
++	/* Now send ioctl to wireless driver to set the ProbeReponse bit field. */
++	if (ioctl_sock >= 0)
++		retVal = wsc_set_oid(RT_OID_WSC_SET_SELECTED_REGISTRAR, (char *)decodeStr, decodeLen);
++
++	if (retVal != 0)
++		goto done;
++	
++	/*
++		Send UPnP repsone to remote UPnP device controller 
++	*/
++	retVal = UpnpAddToActionResponse(out, "SetSelectedRegistrar", WscServiceTypeStr, NULL, NULL);
++	if (retVal != 0)
++	{
++		retVal = -1;
++	}
++	else
++	{
++		BuildSendAndCloseSoapResp(h, *out, strlen(*out));
++		if (*out != NULL)
++		{
++			free(*out);
++		}
++	}
++done:
++	if (inStr)
++	{
++//		free(inStr);
++	}
++	if (decodeStr)
++		free(decodeStr);
++
++	if (retVal != 0)
++		(*errorString) = "Internal Error";
++	
++	return 0;
++	
++}
++
++
++/******************************************************************************
++ * WscDevPutWLANResponse
++ *
++ * Description: 
++ *      When Device in Proxy Mode, the Registrar will use this Action response 
++ *      the M2/M2D, M4, M6, and M8 messages.
++ *
++ * Parameters:
++ *   
++ *    IXML_Document * in -  action request document
++ *    IXML_Document **out - action result document
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *  
++ * Return:
++ *    0 - if success
++ *    -1 - if failure
++ *****************************************************************************/
++int
++WscDevPutWLANResponse(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++	char *inStr = NULL;
++	unsigned char *decodeStr = NULL;
++	int decodeLen, retVal = -1;
++	
++	char *pWscU2KMsg = NULL;
++	RTMP_WSC_U2KMSG_HDR *msgHdr = NULL;
++	int wscU2KMsgLen;
++	int inStrLen=0;	
++
++	(*out) = NULL;
++	(*errorString) = NULL;
++
++	inStr = WSCGetValueFromNameValueList((char *)(h->req_buf + h->req_contentoff), "NewMessage", &inStrLen);
++	ASSERT(inStrLen > 0);
++	if (!inStr)
++	{
++		(*errorString) = "Invalid PutWLANResponse mesg";
++		SoapError(h, 402, "Invalid Args");		
++		return retVal;
++	}
++	decodeLen = ILibBase64Decode((unsigned char *)inStr, inStrLen, &decodeStr);
++	if (decodeLen == 0 || decodeStr == NULL)
++	{
++		goto done;
++	}
++	
++	/* Prepare the msg buffers */
++	wscU2KMsgLen = wscU2KMsgCreate(&pWscU2KMsg, (char *)decodeStr, decodeLen, EAP_FRAME_TYPE_WSC);
++	if (wscU2KMsgLen == 0)
++		goto done;
++
++	// Fill the sessionID, Addr1, and Adde2 to the U2KMsg buffer header.
++	msgHdr = (RTMP_WSC_U2KMSG_HDR *)pWscU2KMsg;
++	msgHdr->envID = 0;
++
++	memcpy(msgHdr->Addr1, HostMacAddr, MAC_ADDR_LEN);
++	memcpy(msgHdr->Addr2, &ipAddr, sizeof(unsigned int));
++	
++	DBGPRINTF(RT_DBG_INFO, "(%s):Ready to send pWscU2KMsg(len=%d) to kernel by ioctl(%d)!\n", __FUNCTION__, 
++				wscU2KMsgLen, ioctl_sock);
++
++	wsc_hexdump("U2KMsg", pWscU2KMsg, wscU2KMsgLen);
++	
++	// Now send the msg to kernel space.
++	if (ioctl_sock >= 0)
++		retVal = wsc_set_oid(RT_OID_WSC_EAPMSG, (char *)pWscU2KMsg, wscU2KMsgLen);
++
++	// Waiting for the response from the kernel space.
++	DBGPRINTF(RT_DBG_INFO, "ioctl retval=%d! senderID=%d!\n", retVal, senderID);
++	if(retVal == 0)
++	{
++		retVal = UpnpAddToActionResponse(out, "PutWLANResponse", WscServiceTypeStr, NULL, NULL);
++	}
++	BuildSendAndCloseSoapResp(h, *out, strlen(*out));
++
++	if (*out != NULL)
++	{
++		free(*out);
++	}
++done:
++
++	if (inStr)
++	{
++//		free(inStr);
++	}
++	if (decodeStr)
++		free(decodeStr);
++	if (pWscU2KMsg)
++		free(pWscU2KMsg);
++	
++	if (retVal == 0)
++		return 0;
++	else {
++		(*errorString) = "Internal Error";
++		return -1;
++	}
++}
++
++
++/******************************************************************************
++ * WscDevPutMessageResp
++ *
++ * Description: 
++ *       This action used by Registrar send WSC_MSG(M2,M4,M6, M8) to Enrollee.
++ *
++ * Parameters:
++ *    
++ *    IXML_Document * in - document of action request
++ *    IXML_Document **out - action result
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *
++ * Return:
++ *    0 - if success
++ *    -1 - if failure
++ *****************************************************************************/
++int 
++WscDevPutMessageResp(
++	IN WscEnvelope *msgEnvelope)
++{
++	int retVal= -1;
++	char *pWscU2KMsg = NULL;
++	char *out = NULL; 
++	struct upnphttp * h = msgEnvelope->h;
++
++
++	DBGPRINTF(RT_DBG_INFO, "Got msg from netlink! envID=0x%x!\n", msgEnvelope->envID);
++
++#if 0
++	( out ) = malloc(2500);
++#endif
++	if ((msgEnvelope->flag != WSC_ENVELOPE_SUCCESS) || 
++		(msgEnvelope->pMsgPtr == NULL) || 
++		(msgEnvelope->msgLen == 0))
++	{
++		goto done;
++	}
++
++	// Response the msg from state machine back to the remote UPnP control device.
++	if (msgEnvelope->pMsgPtr)
++	{
++		unsigned char *encodeStr = NULL;
++		RTMP_WSC_MSG_HDR *rtmpHdr = NULL;		
++		int len;
++
++		DBGPRINTF(RT_DBG_INFO, "(%s):Ready to parse the K2U msg(len=%d)!\n", __FUNCTION__, msgEnvelope->msgLen);
++		wsc_hexdump("WscDevPutMessage-K2UMsg", msgEnvelope->pMsgPtr, msgEnvelope->msgLen);
++		
++		rtmpHdr = (RTMP_WSC_MSG_HDR *)(msgEnvelope->pMsgPtr);
++	
++		if(rtmpHdr->msgType == WSC_OPCODE_UPNP_CTRL || rtmpHdr->msgType == WSC_OPCODE_UPNP_MGMT)
++		{
++			DBGPRINTF(RT_DBG_INFO, "Receive a UPnP Ctrl/Mgmt Msg!\n");
++			goto done;
++		}
++		
++		if(rtmpHdr->msgSubType == WSC_UPNP_DATA_SUB_ACK)
++		{
++			retVal = UpnpAddToActionResponse(&out, "PutMessage", WscServiceTypeStr, NULL, NULL);
++		}
++		else 
++		{
++			len = ILibBase64Encode((unsigned char *)(msgEnvelope->pMsgPtr + sizeof(RTMP_WSC_MSG_HDR)), 
++									rtmpHdr->msgLen, &encodeStr);
++			if (len >0)
++				retVal = UpnpAddToActionResponse(&out, "PutMessage", WscServiceTypeStr, 
++													"NewOutMessage", (char *)encodeStr);
++			if (encodeStr != NULL)
++				free(encodeStr);
++		}
++		
++		if (out)
++			BuildSendAndCloseSoapResp(h, out, strlen(out));
++	} 
++
++done:
++        if (out != NULL)
++        {
++              free(out);
++        }
++
++	if (pWscU2KMsg)
++		free(pWscU2KMsg);
++	wscEnvelopeFree(msgEnvelope);
++	if (retVal == 0)
++	{
++		return retVal;
++	}
++	else
++	{
++		return -1;
++	}
++}
++
++
++
++/******************************************************************************
++ * WscDevPutMessage
++ *
++ * Description: 
++ *       This action used by Registrar request WSC_MSG(M2,M4,M6, M8) from WIFI driver to Enrollee.
++ *
++ * Parameters:
++ *    
++ *    IXML_Document * in - document of action request
++ *    IXML_Document **out - action result
++ *    char **errorString - errorString (in case action was unsuccessful)
++ *
++ * Return:
++ *    0 - if success
++ *    -1 - if failure
++ *****************************************************************************/
++int 
++WscDevPutMessage(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++	char *inStr = NULL;
++	int inStrLen=0;
++	unsigned char *decodeStr = NULL;
++	int decodeLen, retVal = -1;
++	
++	char *pWscU2KMsg = NULL;
++	RTMP_WSC_U2KMSG_HDR *msgHdr = NULL;
++	WscEnvelope *msgEnvelope = NULL;
++	int wscU2KMsgLen;
++	int msgQIdx = -1;
++
++	(*out) = NULL;
++	(*errorString) = NULL;
++
++	inStr = WSCGetValueFromNameValueList((char *)(h->req_buf + h->req_contentoff), "NewInMessage", &inStrLen);
++	ASSERT(inStrLen > 0);
++	if (!inStr)
++	{
++		(*errorString) = "Invalid PutMessage mesg";
++		SoapError(h, 402, "Invalid Args");		
++		return retVal;
++	}
++	decodeLen = ILibBase64Decode((unsigned char *)inStr, inStrLen, &decodeStr);
++	if (decodeLen == 0 || decodeStr == NULL)
++	{
++		goto done;
++	}
++
++	/* Prepare the msg buffers */
++	wscU2KMsgLen = wscU2KMsgCreate(&pWscU2KMsg, (char *)decodeStr, decodeLen, EAP_FRAME_TYPE_WSC);
++	if (wscU2KMsgLen == 0)
++		goto done;
++
++	/* Prepare the msg envelope */
++	if ((msgEnvelope = wscEnvelopeCreate()) == NULL)
++		goto done;
++	
++	/* Lock the msgQ and check if we can get a valid mailbox to insert our request! */
++	if(wscMsgQInsert(msgEnvelope, &msgQIdx)!= WSC_SYS_SUCCESS)
++	{
++		goto done;
++	}
++
++	/* log the socket ID and callback in the msgEnvelope */
++	msgEnvelope->h = h;
++	msgEnvelope->DevCallBack = WscDevPutMessageResp;
++	
++	// Fill the sessionID, Addr1, and Adde2 to the U2KMsg buffer header.
++	msgHdr = (RTMP_WSC_U2KMSG_HDR *)pWscU2KMsg;
++	msgHdr->envID = msgEnvelope->envID;
++	memcpy(msgHdr->Addr1, HostMacAddr, MAC_ADDR_LEN);
++	memcpy(msgHdr->Addr2, &ipAddr, sizeof(unsigned int));
++	
++	DBGPRINTF(RT_DBG_INFO, "(%s):Ready to send pWscU2KMsg(len=%d) to kernel by ioctl(%d)!\n", __FUNCTION__, 
++				wscU2KMsgLen, ioctl_sock);
++	wsc_hexdump("U2KMsg", pWscU2KMsg, wscU2KMsgLen);
++	
++	// Now send the msg to kernel space.
++	if (ioctl_sock >= 0)
++		retVal = wsc_set_oid(RT_OID_WSC_EAPMSG, (char *)pWscU2KMsg, wscU2KMsgLen);
++
++	if (retVal == 0)
++	{
++		/* let main loop do nothing about the current http exchange */
++		
++		#if 1 //Mediatek : porting for miniupnpd 1.6
++		h->state = DO_NOTHING_IN_MAIN_LOOP;
++		#else
++		h->state = 3;		
++		#endif
++
++		// Waiting for the response from the kernel space.
++		DBGPRINTF(RT_DBG_INFO, "%s():ioctl to kernel success, waiting for condition!\n", __FUNCTION__);
++
++		if (inStr)
++//			free(inStr);// I have no right to free this pointer, because the buffer is not alloc by me.
++		if (decodeStr)
++			free(decodeStr);
++		if (pWscU2KMsg)
++			free(pWscU2KMsg);
++		return retVal;
++	}
++	else
++	{
++		DBGPRINTF(RT_DBG_INFO, "%s():ioctl to kernel failed, retVal=%d, goto done!\n", __FUNCTION__, retVal);
++		wscMsgQRemove(Q_TYPE_PASSIVE, msgQIdx);
++		goto done;
++	}
++
++done:
++	if (inStr)
++	{
++//		free(inStr);// I have no right to free this pointer, because the buffer is not alloc by me.
++	}
++	if (decodeStr)
++		free(decodeStr);
++	if (pWscU2KMsg)
++		free(pWscU2KMsg);
++
++	wscEnvelopeFree(msgEnvelope);
++
++	if (retVal == 0)
++	{
++		return retVal;
++	}
++	else
++	{
++		(*errorString) = "Internal Error";
++		return -1;
++	}
++}
++
++
++/******************************************************************************
++ * WscDevGetDeviceInfoResp
++ *
++ * Description: 
++ *       This action callback used to send AP's M1 message to the Control Point(Registrar).
++ *
++ * Parameters:
++ *
++ *    WscEnvelope *msgEnvelope  - response from wifi driver of action request
++ *    
++ *    
++ *    
++ *
++ * Return:
++ *    0 - if success
++ *    -1 - if failure
++ *****************************************************************************/
++int
++WscDevGetDeviceInfoResp(
++	IN WscEnvelope *msgEnvelope)
++{
++	int retVal= -1;
++	char *pWscU2KMsg = NULL;
++	char *out = NULL; 
++	struct upnphttp * h = msgEnvelope->h;
++
++	DBGPRINTF(RT_DBG_INFO, "(%s):Got msg from netlink! envID=0x%x, flag=%d!\n", 
++				__FUNCTION__, msgEnvelope->envID, msgEnvelope->flag);
++
++#if 0
++	( out ) = malloc(2500);
++#endif
++
++	if ((msgEnvelope->flag != WSC_ENVELOPE_SUCCESS) || 
++		(msgEnvelope->pMsgPtr == NULL) || 
++		(msgEnvelope->msgLen == 0))
++	{
++		goto done;
++	}
++
++	// Response the msg from state machine back to the remote UPnP control device.
++	if (msgEnvelope->pMsgPtr)
++	{
++		unsigned char *encodeStr = NULL;
++		RTMP_WSC_MSG_HDR *rtmpHdr = NULL;		
++		int len;
++
++		rtmpHdr = (RTMP_WSC_MSG_HDR *)(msgEnvelope->pMsgPtr);
++		DBGPRINTF(RT_DBG_INFO, "(%s):Ready to parse the K2U msg(TotalLen=%d, headerLen=%d)!\n", __FUNCTION__, 
++						msgEnvelope->msgLen, sizeof(RTMP_WSC_MSG_HDR));
++		DBGPRINTF(RT_DBG_INFO, "\tMsgType=%d!\n" 
++								"\tMsgSubType=%d!\n"
++								"\tipAddress=0x%x!\n"
++								"\tMsgLen=%d!\n",
++								rtmpHdr->msgType, rtmpHdr->msgSubType, rtmpHdr->ipAddr, rtmpHdr->msgLen);
++		
++		if(rtmpHdr->msgType == WSC_OPCODE_UPNP_CTRL || rtmpHdr->msgType == WSC_OPCODE_UPNP_MGMT)
++		{
++			DBGPRINTF(RT_DBG_INFO, "Receive a UPnP Ctrl/Mgmt Msg!\n");
++			goto done;
++		}
++		wsc_hexdump("WscDevGetDeviceInfoResp-K2UMsg", msgEnvelope->pMsgPtr, msgEnvelope->msgLen);
++		
++		if(rtmpHdr->msgSubType == WSC_UPNP_DATA_SUB_ACK)
++		{
++			retVal = UpnpAddToActionResponse(&out, "GetDeviceInfo", WscServiceTypeStr, NULL, NULL);
++		}
++		else
++		{
++			len = ILibBase64Encode((unsigned char *)(msgEnvelope->pMsgPtr + sizeof(RTMP_WSC_MSG_HDR)), 
++									rtmpHdr->msgLen, &encodeStr);
++
++			if (len > 0)
++			{
++				retVal = UpnpAddToActionResponse(&out, "GetDeviceInfo", WscServiceTypeStr, 
++												"NewDeviceInfo", (char *)encodeStr);
++			}
++
++			if (encodeStr != NULL)
++				free(encodeStr);
++		}
++
++		if (out)
++			BuildSendAndCloseSoapResp(h, out, strlen(out));
++	} 
++
++done:
++	if (out != NULL)
++	{
++	    free(out);
++	}
++
++	if (pWscU2KMsg)
++		free(pWscU2KMsg);
++	
++	wscEnvelopeFree(msgEnvelope);
++	
++	if (retVal == 0)
++	{
++		return retVal;
++	}
++	else
++	{
++		return -1;
++	}
++
++}
++
++	
++/******************************************************************************
++ * WscDevGetDeviceInfo
++ *
++ * Description: 
++ *       This action callback used to send AP's M1 message to the Control Point(Registrar).
++ *
++ * Parameters:
++ *
++ *    IXML_Document * in  - document of action request
++ *    uint32 ipAddr       - ipAddr,
++ *    IXML_Document **out - action result
++ *    char **errorString  - errorString (in case action was unsuccessful)
++ *
++ * Return:
++ *    0 - if success
++ *    -1 - if failure
++ *****************************************************************************/
++int
++WscDevGetDeviceInfo(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString)
++{
++	char UPnPIdentity[] = {"WFA-SimpleConfig-Registrar"};
++	int retVal= -1;
++	char *pWscU2KMsg = NULL;
++	WscEnvelope *msgEnvelope = NULL;
++	RTMP_WSC_U2KMSG_HDR *msgHdr = NULL;
++	int wscU2KMsgLen;
++	int msgQIdx = -1;
++	
++	
++	DBGPRINTF(RT_DBG_INFO, "Receive a GetDeviceInfo msg from Remote Upnp Control Point!\n");
++	
++#if 0
++	( out ) = NULL;
++#endif
++	( *errorString ) = NULL;
++
++	/* Prepare the msg buffers */
++	wscU2KMsgLen = wscU2KMsgCreate(&pWscU2KMsg, UPnPIdentity, strlen(UPnPIdentity), EAP_FRAME_TYPE_IDENTITY);
++	if (wscU2KMsgLen == 0)
++		goto done;
++
++	/* Prepare the msg envelope */
++	if ((msgEnvelope = wscEnvelopeCreate()) == NULL)
++		goto done;
++	
++	/* Lock the msgQ and check if we can get a valid mailbox to insert our request! */
++	if (wscMsgQInsert(msgEnvelope, &msgQIdx) != WSC_SYS_SUCCESS)
++	{
++		goto done;
++	}
++
++	/* log the http info and callback at the msgEnvelope */
++	msgEnvelope->h = h;
++	msgEnvelope->DevCallBack = WscDevGetDeviceInfoResp;
++	
++	// Fill the sessionID, Addr1, and Adde2 to the U2KMsg buffer header.
++	msgHdr = (RTMP_WSC_U2KMSG_HDR *)pWscU2KMsg;
++	msgHdr->envID = msgEnvelope->envID;
++	memcpy(msgHdr->Addr1, HostMacAddr, MAC_ADDR_LEN);
++	memcpy(msgHdr->Addr2, &ipAddr, sizeof(int));
++	
++	// Now send the msg to kernel space.
++	DBGPRINTF(RT_DBG_INFO, "(%s):Ready to send pWscU2KMsg(len=%d) to kernel by ioctl(%d)!\n", __FUNCTION__, 
++				wscU2KMsgLen, ioctl_sock);
++	wsc_hexdump("U2KMsg", pWscU2KMsg, wscU2KMsgLen);
++
++	if (ioctl_sock >= 0)
++		retVal = wsc_set_oid(RT_OID_WSC_EAPMSG, pWscU2KMsg, wscU2KMsgLen);
++	else
++		retVal = -1;
++	
++	if (retVal == 0)
++	{
++		/* let main loop do nothing about the current http exchange */
++		h->state = DO_NOTHING_IN_MAIN_LOOP;
++
++		// Waiting for the response from the kernel space.
++		DBGPRINTF(RT_DBG_INFO, "%s():ioctl to kernel success, waiting for response!\n", __FUNCTION__);
++
++		if (pWscU2KMsg)
++			free(pWscU2KMsg);
++		return retVal;
++	}
++	else
++	{
++		DBGPRINTF(RT_DBG_ERROR, "%s():ioctl to kernel failed, retVal=%d, goto done!\n", __FUNCTION__, retVal);
++		wscMsgQRemove(Q_TYPE_PASSIVE, msgQIdx);
++		goto done;
++	}
++
++	/* moved to WscDevGetDeviceInfoResp() */
++
++done:
++	if (pWscU2KMsg)
++		free(pWscU2KMsg);
++	
++	wscEnvelopeFree(msgEnvelope);
++	
++	if (retVal == 0)
++		return retVal;
++	else
++	{
++		(*errorString) = "Internal Error";
++		return -1;
++	}
++}
++/*---------------------------end of action handler---------------------------*/
++
++
++
++/******************************************************************************
++ * WscDevHandleActionReq
++ *
++ * Description: 
++ *       Called during an action request callback.  If the request is for the
++ *       Wsc device and match the ServiceID, then perform the action and respond.
++ *
++ * Parameters:
++ *   ca_event -- The "Upnp_Action_Request" structure
++ *
++ * Return:
++ *    
++ *****************************************************************************/
++static int
++WscDevHandleActionReq(IN struct upnphttp * h)
++{
++	return 0;
++}
++
++
++/******************************************************************************
++ * WscDevCPNodeInsert
++ *
++ * Description: 
++ *       Depends on the UDN String, service identifier and Subscription ID, find 
++ *		 the IP address of the remote Control Point. And insert the Control Point
++ *       into the "WscCPList".  
++ *
++ * NOTE: 
++ *		 Since this function blocks on the mutex WscDevMutex, to avoid a hang this 
++ *		 function should not be called within any other function that currently has 
++ *		 this mutex locked.
++ *
++ *		 Besides, this function use the internal data structure of libupnp-1.3.1 
++ *		 SDK, so if you wanna change the libupnp to other versions, please make sure
++ *		 the data structure refered in this function was not changed!
++ *
++ * Parameters:
++ *   UpnpDevice_Handle  - UpnpDevice handle of WscLocalDevice assigned by UPnP SDK.
++ *   char *UDN			- The UUID string of the Subscription requested.
++ *   char *servId 		- The service Identifier of the Subscription requested.
++ *   char *sid 			- The Subscription ID of the ControlPoint.
++ * 
++ * Return:
++ *   TRUE  - If success
++ *   FALSE - If failure
++ *****************************************************************************/
++static int 
++WscDevCPNodeInsert(
++	IN int device_handle,
++	IN const char *sid)
++{
++//	struct Handle_Info *handle_info;
++	int found = 0;
++	struct upnpCPNode *CPNode, *newCPNode;
++	
++	HandleLock();
++
++	CPNode = WscCPList;
++	while(CPNode)
++	{
++		if (strcmp(CPNode->device.SID, sid) == 0)
++		{
++			found = 1;
++			break;
++		}
++		CPNode = CPNode->next;
++	}
++
++	if (found)
++	{
++		strncpy(CPNode->device.SID, sid, NAME_SIZE);
++	}
++	else
++	{
++		// It's a new subscriber, insert it.
++		if ((newCPNode = malloc(sizeof(struct upnpCPNode))) != NULL)
++		{
++			memset(newCPNode, 0, sizeof(struct upnpCPNode));
++			strncpy(newCPNode->device.SID, sid, NAME_SIZE);
++			newCPNode->next = NULL;
++
++			if(WscCPList)
++			{
++				newCPNode->next = WscCPList->next;
++				WscCPList->next = newCPNode;
++			}
++			else
++			{
++				WscCPList = newCPNode;
++			}
++		}
++		else 
++		{
++			goto Fail;
++		}
++	}
++
++	HandleUnlock();
++	DBGPRINTF(RT_DBG_INFO, "Insert ControlPoint success!\n");
++		
++	return TRUE;
++
++Fail:
++	
++	HandleUnlock();
++	DBGPRINTF(RT_DBG_ERROR, "Insert ControlPoint failed!\n");	
++	return FALSE;
++}
++
++
++
++/******************************************************************************
++ * WscDevCPNodeSearch
++ *
++ * Description: 
++ *   Search for specific Control Point Node by ip address in WscCPList
++ *
++ * Parameters:
++ *   unsigned int ipAddr - ip address of the contorl point we want to seach.
++ *   char *sid           - used to copy the SID string
++ *
++ * Return:
++ *   1 - if found
++ *   0 - if not found
++ *****************************************************************************/
++static int 
++WscDevCPNodeSearch(
++	IN unsigned int ipAddr,
++	OUT char **strSID)
++{
++	struct upnpCPNode *CPNode;
++	int found = 0;
++	
++	
++	CPNode = WscCPList;
++	while (CPNode)
++	{
++		if(CPNode->device.ipAddr == ipAddr)
++		{
++			*strSID = strdup(CPNode->device.SID);
++			found = 1;
++			break;
++		}
++		CPNode = CPNode->next;
++	}
++
++	return found;
++}
++
++
++/******************************************************************************
++ * WscDevCPListRemoveAll
++ *
++ * Description: 
++ *   Remove all Control Point Node in WscCPList
++ *
++ * Parameters:
++ *   None
++ *
++ * Return:
++ *   TRUE
++ *****************************************************************************/
++static int WscDevCPListRemoveAll(void)
++{
++	struct upnpCPNode *CPNode;
++
++	while((CPNode = WscCPList))
++	{
++		WscCPList = CPNode->next;
++		free(CPNode);
++	}
++	
++	return TRUE;
++}
++
++/******************************************************************************
++ * WscDevCPNodeRemove
++ *
++ * Description: 
++ *       Remove the ControlPoint Node in WscCPList depends on the subscription ID.
++ *
++ * Parameters:
++ *   char *SID - The subscription ID of the ControlPoint will be deleted
++ * 
++ * Return:
++ *   TRUE  - If success
++ *   FALSE - If failure
++ *****************************************************************************/
++int WscDevCPNodeRemove(IN char *SID)
++{
++	struct upnpCPNode *CPNode, *prevNode = NULL;
++	
++	CPNode = prevNode = WscCPList;
++
++	if(strcmp(WscCPList->device.SID, SID) == 0)
++	{
++		WscCPList = WscCPList->next;
++		free(CPNode);
++	} 
++	else
++	{
++		while((CPNode = CPNode->next))
++		{
++			if(strcmp(CPNode->device.SID, SID) == 0)
++			{
++				prevNode->next = CPNode->next;
++				free(CPNode);
++				break;
++			}
++			prevNode = CPNode;
++		}
++	}
++
++	return TRUE;
++}
++
++
++/******************************************************************************
++ * dumpDevCPNodeList
++ *
++ * Description: 
++ *       Dump the WscCPList. Used for debug.
++ *
++ * Parameters:
++ *   	void
++ *
++ *****************************************************************************/
++void dumpDevCPNodeList(void)
++{
++	struct upnpCPNode *CPNode;
++	int i=0;
++	
++	printf("Dump The UPnP Subscribed ControlPoint:\n");
++
++	CPNode = WscCPList;	
++	while(CPNode)
++	{
++		i++;
++		printf("ControlPoint Node[%d]:\n", i);
++		printf("\t ->sid=%s\n", CPNode->device.SID);
++		printf("\t ->SubURL=%s\n", CPNode->device.SubURL);
++		printf("\t ->ipAddr=0x%x!\n", CPNode->device.ipAddr);
++		printf("\t ->SubTimeOut=%d\n", CPNode->device.SubTimeOut);
++		CPNode = CPNode->next;
++	}
++	
++	printf("\n-----DumpFinished!\n");
++
++}
++
++
++/******************************************************************************
++ * WscDevStateVarUpdate
++ *
++ * Description: 
++ *       Update the Wsc Device Service State variable, and notify all subscribed 
++ *       Control Points of the updated state.  Note that since this function
++ *       blocks on the mutex WscDevMutex, to avoid a hang this function should 
++ *       not be called within any other function that currently has this mutex 
++ *       locked.
++ *
++ * Parameters:
++ *   variable -- The variable number (WSC_EVENT_WLANEVENT, WSC_EVENT_APSTATUS,
++ *                   WSC_EVENT_STASTATUS)
++ *   value -- The string representation of the new value
++ *
++ *****************************************************************************/
++static int
++WscDevStateVarUpdate(
++	IN unsigned int variable,
++	IN char *value,
++	IN char *SubsId)
++{
++	struct subscriber* sub = NULL;
++	int sub_found = 0;
++	
++	if((variable >= WSC_STATE_VAR_MAXVARS) || (strlen(value) >= WSC_STATE_VAR_MAX_STR_LEN))
++		return (0);
++
++	strcpy(wscLocalDevice.services.StateVarVal[variable], value);
++
++	if (SubsId == NULL)
++	{
++		upnp_event_var_change_notify(EWSC);
++	}
++	else 
++	{
++		sub_found = WscDevSubscriberSearch(SubsId, &sub);
++
++		if ((sub_found)/* && (sub->service == EWSC)*/)
++		{
++			if (sub != NULL)
++			{
++				if ((sub->service == EWSC) && (sub->notify == NULL))
++				{
++					upnp_event_create_notify(sub);
++				}
++			}
++			else
++			{
++				DBGPRINTF(RT_DBG_ERROR, "\n%s() : ERROR!!! : sub = NULL!.\n", __FUNCTION__); 
++			}
++		}
++		else if (!sub_found)
++		{
++			DBGPRINTF(RT_DBG_ERROR, "\n%s() : ERROR!!! : The subscriber can not be found in subscriberlist., SubsId=%s\n", __FUNCTION__,SubsId);
++			if (sub) 
++			{
++				DBGPRINTF(RT_DBG_ERROR, "\n%s() : ERROR!!! : The subscriber can not be found in subscriberlist., sub->service=%d, SubsId=%s\n", __FUNCTION__,sub->service,SubsId);
++			}
++			else
++			{
++				DBGPRINTF(RT_DBG_ERROR, "\n%s() : ERROR!!! : sub = NULL!. \n", __FUNCTION__);
++			}	
++		}
++	}
++
++    return (1);
++}
++
++
++/*
++	The format of UPnP WLANEvent message send to Remote Registrar.
++	  WLANEvent:
++		=>This variable represents the concatenation of the WLANEventType, WLANEventMac and the 
++		  802.11 WSC message received from the Enrollee & forwarded by the proxy over UPnP.
++		=>the format of this variable
++			->represented as base64(WLANEventType || WLANEventMAC || Enrollee's message).
++	  WLANEventType:
++		=>This variable represents the type of WLANEvent frame that was received by the proxy on 
++		  the 802.11 network.
++		=>the options for this variable
++			->1: 802.11 WCN-NET Probe Frame
++			->2: 802.11 WCN-NET 802.1X/EAP Frame
++	  WLANEventMAC:
++		=>This variable represents the MAC address of the WLAN Enrollee that generated the 802.11 
++	 	  frame that was received by the proxy.
++	 	=>Depends on the WFAWLANConfig:1  Service Template Version 1.01, the format is
++ 			->"xx:xx:xx:xx:xx:xx", case-independent, 17 char
++*/	
++int WscEventCtrlMsgRecv(
++	IN char *pBuf,
++	IN int  bufLen)
++{
++
++	DBGPRINTF(RT_DBG_INFO, "Receive a Control Message!\n");
++	return 0;
++
++}
++
++
++//receive a WSC message and send it to remote UPnP Contorl Point
++int WscEventDataMsgRecv(
++	IN char *pBuf,
++	IN int  bufLen)
++{
++	RTMP_WSC_MSG_HDR *pHdr = NULL;
++	unsigned char *encodeStr = NULL, *pWscMsg = NULL, *pUPnPMsg = NULL;
++	int encodeLen = 0, UPnPMsgLen = 0;
++	int retVal;
++	uint32 wscLen;
++	char *strSID = NULL;
++	unsigned char includeMAC = 0;
++	char curMacStr[18];
++	
++	if ((WscUPnPOpMode & WSC_UPNP_OPMODE_DEV) == 0)
++		return -1;
++		
++	pHdr = (RTMP_WSC_MSG_HDR *)pBuf;
++
++#if 0 //Mediatek : Sync with wscd, skip search control point by IP, and send the upnp data to all  control points
++	// Find the SID.
++	if(WscDevCPNodeSearch(pHdr->ipAddr, &strSID) == 0)
++	{
++		DBGPRINTF(RT_DBG_INFO, "%s(): Didn't find the SID by ip(0x%x)!\n", __FUNCTION__, pHdr->ipAddr);
++	}
++	else
++	{
++		DBGPRINTF(RT_DBG_INFO, "%s(): The SID(%s) by ip(0x%x)!\n", __FUNCTION__, strSID, pHdr->ipAddr);
++	}
++#endif	
++	DBGPRINTF(RT_DBG_INFO, "%s:Receive a Data event, msgSubType=%d!\n", __FUNCTION__, pHdr->msgSubType);
++
++	memset(curMacStr, 0 , sizeof(curMacStr));
++	if ((pHdr->msgSubType & WSC_UPNP_DATA_SUB_INCLUDE_MAC) == WSC_UPNP_DATA_SUB_INCLUDE_MAC)
++	{
++		if (pHdr->msgLen < 6){
++			DBGPRINTF(RT_DBG_ERROR, "pHdr->msgSubType didn't have enoguh length!\n", pHdr->msgLen);
++			return -1;
++		}
++		includeMAC = 1;
++		pHdr->msgSubType &= 0x00ff;
++		pWscMsg = (unsigned char *)(pBuf + sizeof(RTMP_WSC_MSG_HDR));
++		snprintf(curMacStr, 18, "%02x:%02x:%02x:%02x:%02x:%02x", pWscMsg[0], pWscMsg[1], pWscMsg[2], pWscMsg[3],pWscMsg[4],pWscMsg[5]);	
++	}
++	else
++	{
++		memcpy(&curMacStr[0], CfgMacStr, 17);
++	}
++
++	
++	if (pHdr->msgSubType == WSC_UPNP_DATA_SUB_NORMAL || 
++		pHdr->msgSubType == WSC_UPNP_DATA_SUB_TO_ALL ||
++		pHdr->msgSubType == WSC_UPNP_DATA_SUB_TO_ONE)
++	{
++
++		DBGPRINTF(RT_DBG_INFO, "(%s):Ready to parse the K2U msg(len=%d)!\n", __FUNCTION__, bufLen);
++
++		pWscMsg = (unsigned char *)(pBuf + sizeof(RTMP_WSC_MSG_HDR));
++		//Get the WscData Length
++		wscLen = pHdr->msgLen;
++		
++		if (includeMAC)
++		{
++			wscLen -= MAC_ADDR_LEN;
++			pWscMsg += MAC_ADDR_LEN;
++		}
++		
++		DBGPRINTF(RT_DBG_INFO, "(%s): pWscMsg Len=%d!\n", __FUNCTION__, wscLen);
++			
++			
++		UPnPMsgLen = wscLen + 18;
++		pUPnPMsg = malloc(UPnPMsgLen);
++		if(pUPnPMsg)
++		{
++			memset(pUPnPMsg, 0, UPnPMsgLen);
++			pUPnPMsg[0] = WSC_EVENT_WLANEVENTTYPE_EAP;
++			memcpy(&pUPnPMsg[1], &curMacStr[0], 17);
++
++			//Copy the WscMsg to pUPnPMsg buffer
++			memcpy(&pUPnPMsg[18], pWscMsg, wscLen);
++			wsc_hexdump("UPnP WLANEVENT Msg", (char *)pUPnPMsg, UPnPMsgLen);
++
++			//encode the message use base64
++			encodeLen = ILibBase64Encode(pUPnPMsg, UPnPMsgLen, &encodeStr);
++			
++			//Send event out
++			if (encodeLen > 0){
++				DBGPRINTF(RT_DBG_INFO, "EAP->Msg=%s!\n", encodeStr);
++				retVal = WscDevStateVarUpdate(WSC_EVENT_WLANEVENT, (char *)encodeStr, strSID);
++			}
++			if (encodeStr != NULL)
++				free(encodeStr);
++			free(pUPnPMsg);
++		}
++	}
++	
++	if (strSID)
++		free(strSID);
++	
++	return 0;
++	
++}
++
++
++/*
++	Format of iwcustom msg WSC RegistrarSelected message:
++	1. The Registrar ID which send M2 to the Enrollee(4 bytes):
++								
++			  4
++		+-----------+
++		|RegistrarID|
++*/
++static int WscEventMgmt_RegSelect(	
++	IN char *pBuf,
++	IN int  bufLen)
++{
++	char *pWscMsg = NULL;
++	int registrarID = 0;
++		
++	if ((WscUPnPOpMode & WSC_UPNP_OPMODE_DEV) == 0)
++		return -1;
++	
++	DBGPRINTF(RT_DBG_INFO, "(%s):Ready to parse the K2U msg(len=%d)!\n", __FUNCTION__, bufLen);
++	wsc_hexdump("WscEventMgmt_RegSelect-K2UMsg", pBuf, bufLen);
++
++	pWscMsg = (char *)(pBuf + sizeof(RTMP_WSC_MSG_HDR));
++
++	registrarID = *((int *)pWscMsg);
++	DBGPRINTF(RT_DBG_INFO, "The registrarID=%d!\n", registrarID);
++	
++	return 0;
++
++}
++
++/*
++	Format of iwcustom msg WSC clientJoin message:
++	1. SSID which station want to probe(32 bytes):
++			<SSID string>
++		*If the length if SSID string is small than 32 bytes, fill 0x0 for remaining bytes.
++	2. Station MAC address(6 bytes):
++	3. Status:
++		Value 1 means change APStatus as 1. 
++		Value 2 means change STAStatus as 1.
++		Value 3 means trigger msg.
++								
++			32         6       1 
++		+----------+--------+------+
++		|SSIDString| SrcMAC |Status|
++*/
++static int WscEventMgmt_ConfigReq(
++	IN char *pBuf,
++	IN int  bufLen)
++{
++	unsigned char *encodeStr = NULL, *pWscMsg = NULL, *pUPnPMsg = NULL;
++	int encodeLen = 0, UPnPMsgLen = 0;
++	int retVal;
++	unsigned char Status;
++	char triggerMac[18];
++		
++	
++	DBGPRINTF(RT_DBG_INFO, "(%s):Ready to parse the K2U msg(len=%d)!\n", __FUNCTION__, bufLen);
++	wsc_hexdump("WscEventMgmt_ConfigReq-K2UMsg", pBuf, bufLen);
++
++	pWscMsg = (unsigned char *)(pBuf + sizeof(RTMP_WSC_MSG_HDR));
++
++	memset(&triggerMac[0], 0, 18);
++	memcpy(&triggerMac[0], pWscMsg, 18);
++	//Skip the SSID field
++	pWscMsg += 32;
++			
++#if 0
++	// Get the SrcMAC and copy to the WLANEVENTMAC, in format "xx:xx:xx:xx:xx:xx", case-independent, 17 char.
++	memset(CfgMacStr, 0 , sizeof(CfgMacStr));
++	snprintf(CfgMacStr, 18, "%02x:%02x:%02x:%02x:%02x:%02x", pWscMsg[0], pWscMsg[1], pWscMsg[2],
++															pWscMsg[3],pWscMsg[4],pWscMsg[5]);
++#endif
++	//Skip the SrcMAC field
++	pWscMsg += 6;
++
++	//Change APStatus and STAStatus
++	Status = *(pWscMsg);
++	DBGPRINTF(RT_DBG_INFO, "(%s): Status =%d!\n", __FUNCTION__, Status);
++
++	if ((WscUPnPOpMode & WSC_UPNP_OPMODE_DEV) && (strlen(triggerMac) == 0))
++	{
++		strcpy(wscLocalDevice.services.StateVarVal[WSC_EVENT_APSTATUS], "0");
++		strcpy(wscLocalDevice.services.StateVarVal[WSC_EVENT_STASTATUS], "0");
++
++		/* not needed right now */
++#if 0
++		if(Status == 3)
++		{
++			/* This "ConfigReq" is the trigger msg, we should send BYEBYE to all external registrar for 
++				reset the cache status of those Vista devices. Then re-send Notify out.
++			*/
++	    	retVal = AdvertiseAndReply(-1, wscDevHandle, 0, (struct sockaddr_in *)NULL,(char *)NULL, 
++									(char *)NULL, (char *)NULL, defAdvrExpires);
++		    retVal = AdvertiseAndReply(1, wscDevHandle, 0, (struct sockaddr_in *)NULL,(char *)NULL, 
++									(char *)NULL, (char *)NULL, defAdvrExpires);
++		}
++#endif
++		//Prepare the message.
++		UPnPMsgLen = 18 + sizeof(wscAckMsg);
++		pUPnPMsg = malloc(UPnPMsgLen);
++		
++		if(pUPnPMsg)
++		{
++			memset(pUPnPMsg, 0, UPnPMsgLen);
++			pUPnPMsg[0] = WSC_EVENT_WLANEVENTTYPE_EAP;
++			memcpy(&pUPnPMsg[1], CfgMacStr, 17);
++
++			//jump to the WscProbeReqData and copy to pUPnPMsg buffer
++			pWscMsg++;	
++			memcpy(&pUPnPMsg[18], wscAckMsg, sizeof(wscAckMsg));
++			wsc_hexdump("UPnP WLANEVENT Msg", (char *)pUPnPMsg, UPnPMsgLen);
++					
++			//encode the message use base64
++			encodeLen = ILibBase64Encode(pUPnPMsg, UPnPMsgLen, &encodeStr);
++			if(encodeLen > 0)
++				DBGPRINTF(RT_DBG_INFO, "ConfigReq->Msg=%s!\n", encodeStr);
++			strcpy(wscLocalDevice.services.StateVarVal[WSC_EVENT_WLANEVENT], (const char *)encodeStr);
++			
++			//Send event out
++			if (encodeLen > 0)
++				retVal = WscDevStateVarUpdate(WSC_EVENT_WLANEVENT, (char *)encodeStr, NULL);
++
++			if (encodeStr != NULL)
++				free(encodeStr);
++			free(pUPnPMsg);
++		}
++	}
++
++	if ((WscUPnPOpMode & WSC_UPNP_OPMODE_CP) && (strlen(triggerMac) > 0))
++	{
++		
++	}
++
++	DBGPRINTF(RT_DBG_INFO, "<-----End(%s)\n", __FUNCTION__);
++	return 0;
++}
++
++/*
++	Format of iwcustom msg WSC ProbeReq message:
++	1. SSID which station want to probe(32 bytes):
++			<SSID string>
++		*If the length if SSID string is small than 32 bytes, fill 0x0 for remaining bytes.
++	2. Station MAC address(6 bytes):
++	3. element ID(OID)(1 byte):
++			val=0xDD for eID
++			val=other values, high byte of length fields.
++	4. Length of "WscProbeReqData" in the probeReq packet(1 byte):
++	5. "WscProbeReqData" info in ProbeReq:
++								
++			32        6      1          1          variable length
++		+----------+--------+---+-----------------+----------------------+
++		|SSIDString| SrcMAC |eID|LenOfWscProbeData|    WscProbeReqData   |
++
++*/
++static int WscEventMgmt_ProbreReq(
++	IN char *pBuf,
++	IN int  bufLen)
++{
++	unsigned char *encodeStr = NULL, *pWscMsg = NULL, *pUPnPMsg = NULL;
++	char srcMacStr[18];
++	int encodeLen = 0, UPnPMsgLen = 0;
++	int retVal;
++	unsigned short wscLen;
++		
++	if ((WscUPnPOpMode & WSC_UPNP_OPMODE_DEV) == 0)
++		return -1;
++	
++	DBGPRINTF(RT_DBG_INFO, "(%s):Ready to parse the K2U msg(len=%d)!\n", __FUNCTION__, bufLen);
++	wsc_hexdump("WscMgmtEvent_ProbreReq-K2UMsg", pBuf, bufLen);
++
++	pWscMsg = (unsigned char *)(pBuf + sizeof(RTMP_WSC_MSG_HDR));
++	//Skip the SSID field
++	pWscMsg += 32;
++			
++	/* Get the SrcMAC and copy to the WLANEVENTMAC, 
++		depends on the WFAWLANConfig:1  Service Template Version 1.01, 
++		the MAC format is "xx:xx:xx:xx:xx:xx", case-independent, 17 char.
++	*/
++	memset(srcMacStr, 0 , sizeof(srcMacStr));
++	snprintf(srcMacStr, 18, "%02x:%02x:%02x:%02x:%02x:%02x", pWscMsg[0], pWscMsg[1], pWscMsg[2],pWscMsg[3],pWscMsg[4],pWscMsg[5]);
++	DBGPRINTF(RT_DBG_INFO, "ProbeReq->Mac=%s!\n", srcMacStr);
++
++	//Skip the SrcMAC field
++	pWscMsg += 6;
++
++	//Get the WscProbeData Length
++	if (*pWscMsg == 0xdd)
++		wscLen = (unsigned char)(*(pWscMsg + 1));
++	else
++		wscLen = ((*(pWscMsg))<<8) + (*(pWscMsg + 1));
++
++	DBGPRINTF(RT_DBG_INFO, "(%s): pWscHdr Len=%d!\n", __FUNCTION__, wscLen);
++		
++	UPnPMsgLen = wscLen + 18;
++	pUPnPMsg = malloc(UPnPMsgLen);
++	
++	if(pUPnPMsg)
++	{
++		memset(pUPnPMsg, 0, UPnPMsgLen);
++		pUPnPMsg[0] = WSC_EVENT_WLANEVENTTYPE_PROBE;
++		memcpy(&pUPnPMsg[1], srcMacStr, 17);
++
++		//jump to the WscProbeReqData and copy to pUPnPMsg buffer
++		pWscMsg += 2;
++		memcpy(&pUPnPMsg[18], pWscMsg, wscLen);
++		wsc_hexdump("UPnP WLANEVENT Msg", (char *)pUPnPMsg, UPnPMsgLen);
++				
++		//encode the message use base64
++		encodeLen = ILibBase64Encode(pUPnPMsg, UPnPMsgLen, &encodeStr);
++			
++		//Send event out
++		if (encodeLen > 0){
++			DBGPRINTF(RT_DBG_INFO, "ProbeReq->Msg=%s!\n", encodeStr);
++			retVal = WscDevStateVarUpdate(WSC_EVENT_WLANEVENT, (char *)encodeStr, NULL);
++		}
++		if (encodeStr != NULL)
++			free(encodeStr);
++		free(pUPnPMsg);
++	}
++	
++	return 0;
++	
++}
++
++
++int WscEventMgmtMsgRecv(
++	IN char *pBuf,
++	IN int  bufLen)
++{
++	RTMP_WSC_MSG_HDR *pHdr = NULL;	
++	
++	pHdr = (RTMP_WSC_MSG_HDR *)pBuf;
++	if (!pHdr)
++		return -1;
++
++	if (pHdr->msgType != WSC_OPCODE_UPNP_MGMT)
++		return -1;
++
++	DBGPRINTF(RT_DBG_INFO, "%s:Receive a MGMT event, msgSubType=%d\n", __FUNCTION__, pHdr->msgSubType);
++	switch(pHdr->msgSubType)
++	{
++		case WSC_UPNP_MGMT_SUB_PROBE_REQ:
++			WscEventMgmt_ProbreReq(pBuf, bufLen);
++			break;
++		case WSC_UPNP_MGMT_SUB_CONFIG_REQ:
++			WscEventMgmt_ConfigReq(pBuf, bufLen);
++			break;
++		case WSC_UPNP_MGMT_SUB_REG_SELECT:
++			WscEventMgmt_RegSelect(pBuf, bufLen);
++			break;
++		default:
++			DBGPRINTF(RT_DBG_ERROR, "Un-Supported WSC Mgmt event type(%d)!\n", pHdr->msgSubType);
++			break;
++	}
++
++	return 0;
++}
++
++
++/******************************************************************************
++ * WscDevHandleSubscriptionReq
++ *
++ * Description: 
++ *       Called during a subscription request callback.  If the subscription 
++ *       request is for the Wsc device and match the serviceId, then accept it.
++ *
++ * Parameters:
++ *   sr_event -- The "Upnp_Subscription_Request" structure
++ *
++ * Return:
++ *   TRUE 
++ *****************************************************************************/
++int
++WscDevHandleSubscriptionReq(IN struct upnphttp * h)
++{
++	/*  Insert this Control Point into WscCPList*/
++	WscDevCPNodeInsert(wscDevHandle, (char *)(&h->req_SIDOff));
++	dumpDevCPNodeList();
++
++	return TRUE;
++}
++
++
++/******************************************************************************
++ * WscDevServiceHandler
++ *
++ * Description: 
++ *       The callback handler registered with the SDK while registering
++ *       root device.  Dispatches the request to the appropriate procedure
++ *       based on the value of EventType. The four requests handled by the 
++ *       device are: 
++ *             1) Event Subscription requests.  
++ *             2) Get Variable requests. 
++ *             3) Action requests.
++ *
++ * Parameters:
++ *
++ *   EventType - The type of callback event
++ *   Event     - Data structure containing event data
++ *   Cookie    - Optional data specified during callback registration
++ *
++ * Return:
++ *   Zero 
++ *****************************************************************************/
++int WscDevServiceHandler(
++	struct upnphttp * h)
++{
++
++	switch (h->req_command) 
++	{
++		case ESubscribe:
++			WscDevHandleSubscriptionReq(h);
++			break;
++
++		case EUnSubscribe:
++			WscDevCPNodeRemove((char *)(&h->req_SIDOff));
++			break;
++
++		case EGet:
++		case EPost:
++			/* do nothing */
++			WscDevHandleActionReq(h);
++			break;
++
++		default:
++			DBGPRINTF(RT_DBG_ERROR, "Error in WscDevServiceHandler: unknown event type %d\n", h->req_command);
++	}
++
++	return (0);
++}
++
++
++/******************************************************************************
++ * WscLocalDevServTbInit
++ *
++ * Description: 
++ *     Initializes the service table for the Wsc UPnP service.
++ *     Note that knowledge of the service description is assumed. 
++ *
++ * Paramters:
++ *     None
++ *
++ * Return:
++ *      always TRUE.
++ *****************************************************************************/
++static int WscLocalDevServTbInit(void)
++{
++	unsigned int i = 0;
++
++	for (i = 0; i < WSC_STATE_VAR_MAXVARS; i++)
++	{
++		wscLocalDevice.services.StateVarVal[i] = wscStateVarCont[i];
++		strncpy(wscLocalDevice.services.StateVarVal[i], wscStateVarDefVal[i], WSC_STATE_VAR_MAX_STR_LEN-1);
++	}
++
++	/* replaced by soapMethods[] in upnpsoap.c */
++		
++	return TRUE;
++}
++
++
++/******************************************************************************
++ * WscUPnPDevStop
++ *
++ * Description: 
++ *     Stops the device. Uninitializes the sdk. 
++ *
++ * Parameters:
++ *     None
++ * Return:
++ *     TRUE 
++ *****************************************************************************/
++int WscUPnPDevStop(void)
++{
++//	UpnpUnRegisterRootDevice(wscDevHandle);
++	WscDevCPListRemoveAll();
++	
++	return 0;
++}
++
++/******************************************************************************
++ * WscUPnPDevStart
++ *
++ * Description: 
++ *      Registers the UPnP device, and sends out advertisements.
++ *
++ * Parameters:
++ *
++ *   char *ipAddr 		 - ip address to initialize the Device Service.
++ *   unsigned short port - port number to initialize the Device Service.
++ *   char *descDoc  	 - name of description document.
++ *                   		If NULL, use default description file name. 
++ *   char *webRootDir 	 - path of web directory.
++ *                   		If NULL, use default PATH.
++ * Return:
++ *    success - WSC_SYS_SUCCESS
++ *    failed  - WSC_SYS_ERROR
++ *****************************************************************************/
++int WscUPnPDevStart(
++	IN char *ipAddr,
++	IN unsigned short port,
++	IN char *descDoc,
++	IN char *webRootDir)
++{
++	int ret, strLen = 0;
++	char descDocUrl[WSC_UPNP_DESC_URL_LEN] = {0};
++	char udnStr[WSC_UPNP_UUID_STR_LEN]={0};
++
++	if (descDoc == NULL)
++		descDoc = DEFAULT_DESC_FILE_NAME;
++
++	if (webRootDir == NULL)
++		webRootDir = DEFAULT_WEB_ROOT_DIR;
++
++	memset(CfgMacStr, 0 , sizeof(CfgMacStr));
++	snprintf(CfgMacStr, 18, "%02x:%02x:%02x:%02x:%02x:%02x", HostMacAddr[0], HostMacAddr[1], 
++				HostMacAddr[2],HostMacAddr[3],HostMacAddr[4],HostMacAddr[5]);
++//peter : 0523	
++//	memset(&HostDescURL[0], 0, WSC_UPNP_DESC_URL_LEN);
++
++#ifdef USE_XML_TEMPLATE
++	memset(udnStr, 0, WSC_UPNP_UUID_STR_LEN);
++	ret = wsc_get_oid(RT_OID_WSC_UUID, &udnStr[5], &strLen);
++	if (ret == 0)
++	{
++		memcpy(&udnStr[0], "uuid:", 5);
++		DBGPRINTF(RT_DBG_PKT, "UUID Str=%s!\n", udnStr);
++
++		/* We replace the uuid string generated by init() of miniupnpd. */
++		strncpy(wps_uuidvalue+5, &udnStr[5], strLen);
++		ASSERT((*(wps_uuidvalue+5+strLen-1)) == '\0');
++		DBGPRINTF(RT_DBG_PKT, "wps_uuidvalue[]=%s!\n", wps_uuidvalue);
++	}
++	else
++	{
++		DBGPRINTF(RT_DBG_ERROR,  "Get UUID string failed -- ret=%d\n", ret);
++		goto failed;
++	}
++#if 0//peter : 0523
++	memset(descDocUrl, 0, WSC_UPNP_DESC_URL_LEN);
++	snprintf(descDocUrl, WSC_UPNP_DESC_URL_LEN, "http://%s:%d/WFADeviceDesc.xml", ipAddr, port);
++	strcpy(&HostDescURL[0], &descDocUrl[0]);
++	DBGPRINTF(RT_DBG_PKT, "Registering the RootDevice\n\t with descDocUrl: %s\n", descDocUrl);
++#endif
++#else
++//peter : 0523
++//	snprintf(descDocUrl, WSC_UPNP_DESC_URL_LEN, "http://%s:%d/%s", ipAddr, port, descDoc);
++//	strcpy(&HostDescURL[0], &descDocUrl[0]);
++#endif /* USE_XML_TEMPLATE */
++//peter : 0523
++//	DBGPRINTF(RT_DBG_PKT, "HostDescURL Prepared\n");
++
++	memset(&wscLocalDevice, 0, sizeof(wscLocalDevice));
++	WscLocalDevServTbInit();	
++	DBGPRINTF(RT_DBG_PKT, "WscLocalDevServTbInit Initialized\n");
++
++	//Init the ControlPoint List.
++	WscCPList = NULL;
++
++	return 0;
++
++failed:
++	
++	return WSC_SYS_ERROR;
++}
++
+Index: lede/build_dir/target-aarch64_cortex-a53_musl/miniupnpd-2.2.1/wsc/wsc_upnp_device.h
+===================================================================
+--- /dev/null
++++ b/wsc/wsc_upnp_device.h
+@@ -0,0 +1,98 @@
++
++
++#ifndef __WSC_UPNP_DEVICE_H__
++#define __WSC_UPNP_DEVICE_H__
++
++int
++WscDevGetAPSettings(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++    OUT IXML_Document * out,
++    OUT char **errorString );
++int
++WscDevGetSTASettings(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++int
++WscDevSetAPSettings(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++int
++WscDevDelAPSettings(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++int
++WscDevSetSTASettings(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++int
++WscDevRebootAP(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++int 
++WscDevResetAP(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++int
++WscDevRebootSTA(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++int
++WscDevResetSTA(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++int WscDevSetSelectedRegistrar(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++int
++WscDevPutWLANResponse(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++
++int
++WscDevPutMessageResp(
++	IN WscEnvelope *msgEnvelope);
++
++int 
++WscDevPutMessage(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++
++int
++WscDevGetDeviceInfoResp(
++	IN WscEnvelope *envelope);
++
++int
++WscDevGetDeviceInfo(
++	IN struct upnphttp * h,
++	IN uint32 ipAddr,
++	OUT IXML_Document * out,
++	OUT char **errorString);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
