blob: 0163bf966a7ee981021695f944d8582e81e8e90e [file] [log] [blame]
/*
* Wl server for linux
*
* Broadcom Proprietary and Confidential. Copyright (C) 2017,
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom;
* the contents of this file may not be disclosed to third parties, copied
* or duplicated in any form, in whole or in part, without the prior
* written permission of Broadcom.
*
*
* <<Broadcom-WL-IPTag/Proprietary:>>
*
* $Id: wlu_server_linux.c 659779 2016-09-15 23:33:28Z $
*/
/* Revision History:Linux port of Remote wl server
*
* Date Author Description
*
* 27-Dec-2007 Suganthi Version 0.1
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <malloc.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <net/if.h>
#ifndef TARGETENV_android
#include <linux/types.h>
#endif
#include <linux/sockios.h>
#include <linux/ethtool.h>
#include <typedefs.h>
#include <wlioctl.h>
#include <dhdioctl.h>
#include <proto/ethernet.h>
#include <bcmendian.h>
#include <bcmutils.h>
#include <bcmcdc.h>
#include <proto/802.11.h>
#include "wlu_remote.h"
#define DEV_TYPE_LEN 3 /* length for devtype 'wl'/'et' */
#define BUF_LENGTH 1000
#define dtoh32(i) i
#ifdef IL_BIGENDIAN
bool swap = FALSE;
#endif
const char *rwl_server_name;
void *g_wl_handle;
unsigned short defined_debug = DEBUG_ERR | DEBUG_INFO;
extern int remote_server_exec(int argc, char **argv, void *ifr);
/* Global to have the PID of the current sync command
* This is required in case the sync command fails to respond,
* the alarm handler shall kill the PID upon a timeout
*/
int g_shellsync_pid;
unsigned char g_return_stat = 0;
static void
syserr(char *s)
{
fprintf(stderr, "%s: ", rwl_server_name);
perror(s);
exit(errno);
}
/* The handler for DHD commands */
int
dhd_ioctl(void *dhd, int cmd, void *buf, int len, bool set)
{
struct ifreq *ifr = (struct ifreq *) dhd;
dhd_ioctl_t ioc;
int ret = 0;
int s;
/* open socket to kernel */
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
syserr("socket");
/* do it */
ioc.cmd = cmd;
ioc.buf = buf;
ioc.len = len;
ioc.set = set;
ioc.driver = DHD_IOCTL_MAGIC;
ifr->ifr_data = (caddr_t) &ioc;
if ((ret = ioctl(s, SIOCDEVPRIVATE, ifr)) < 0) {
if (cmd != DHD_GET_MAGIC) {
ret = BCME_IOCTL_ERROR;
}
}
/* cleanup */
close(s);
return ret;
}
int
wl_ioctl(void *wl, int cmd, void *buf, int len, bool set)
{
struct ifreq *ifr = (struct ifreq *) wl;
wl_ioctl_t ioc;
int ret = 0;
int s;
/* open socket to kernel */
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
syserr("socket");
/* do it */
ioc.cmd = cmd;
ioc.buf = buf;
ioc.len = len;
ioc.set = set;
ifr->ifr_data = (caddr_t) &ioc;
if ((ret = ioctl(s, SIOCDEVPRIVATE, ifr)) < 0) {
if (cmd != WLC_GET_MAGIC) {
ret = BCME_IOCTL_ERROR;
}
}
/* cleanup */
close(s);
return ret;
}
/* Functions copied from wlu.c to check for the driver adapter in the server machine */
int
wl_check(void *wl)
{
int ret;
int val;
if ((ret = wl_ioctl(wl, WLC_GET_MAGIC, &val, sizeof(int), FALSE)) < 0)
return ret;
#ifdef IL_BIGENDIAN
/* Detect if IOCTL swapping is necessary */
if (val == (int)bcmswap32(WLC_IOCTL_MAGIC))
{
val = bcmswap32(val);
swap = TRUE;
}
#endif
if (val != WLC_IOCTL_MAGIC)
return -1;
if ((ret = wl_ioctl(wl, WLC_GET_VERSION, &val, sizeof(int), FALSE)) < 0)
return ret;
val = dtoh32(val);
if (val > WLC_IOCTL_VERSION) {
fprintf(stderr, "Version mismatch, please upgrade\n");
return -1;
}
return 0;
}
static int
wl_get_dev_type(char *name, void *buf, int len)
{
int s;
int ret;
struct ifreq ifr;
struct ethtool_drvinfo info;
/* open socket to kernel */
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
syserr("socket");
/* get device type */
memset(&info, 0, sizeof(info));
info.cmd = ETHTOOL_GDRVINFO;
ifr.ifr_data = (caddr_t)&info;
strncpy(ifr.ifr_name, name, IFNAMSIZ);
if ((ret = ioctl(s, SIOCETHTOOL, &ifr)) < 0) {
/* print a good diagnostic if not superuser */
if (errno == EPERM)
syserr("wl_get_dev_type");
*(char *)buf = '\0';
} else {
strncpy(buf, info.driver, len);
}
close(s);
return ret;
}
static void
wl_find_adapter(struct ifreq *ifr)
{
char proc_net_dev[] = "/proc/net/dev";
FILE *fp;
char buf[BUF_LENGTH], *c, *name;
char dev_type[DEV_TYPE_LEN];
ifr->ifr_name[0] = '\0';
if (!(fp = fopen(proc_net_dev, "r")))
return;
/* eat first two lines */
if (!fgets(buf, sizeof(buf), fp) ||
!fgets(buf, sizeof(buf), fp)) {
fclose(fp);
return;
}
while (fgets(buf, sizeof(buf), fp)) {
c = buf;
while (isspace(*c))
c++;
if (!(name = strsep(&c, ":")))
continue;
strncpy(ifr->ifr_name, name, IFNAMSIZ-1);
ifr->ifr_name[IFNAMSIZ-1] = 0;
/*
* If no -i interface, prioritize ethX, not say wl1.2
* wlanX is Android
*/
if (!strncmp(name, "wl", 2) && strncmp(name, "wlan", 4)) {
continue;
}
if (wl_get_dev_type(name, dev_type, DEV_TYPE_LEN) >= 0 &&
!strncmp(dev_type, "wl", 2))
if (wl_check((void *) ifr) == 0)
break;
ifr->ifr_name[0] = '\0';
}
fclose(fp);
}
int
wl_get(void *wl, int cmd, void *buf, int len)
{
int error;
error = wl_ioctl(wl, cmd, buf, len, FALSE);
return error;
}
int
wl_set(void *wl, int cmd, void *buf, int len)
{
int error;
error = wl_ioctl(wl, cmd, buf, len, TRUE);
return error;
}
extern int set_ctrlc;
void handle_ctrlc(int unused)
{
UNUSED_PARAMETER(unused);
set_ctrlc = 1;
return;
}
volatile sig_atomic_t g_sig_chld = 1;
void rwl_chld_handler(int num)
{
int child_status;
UNUSED_PARAMETER(num);
/* g_return_stat is being set with the return status of sh commands */
waitpid(g_shellsync_pid, &child_status, WNOHANG);
if (WIFEXITED(child_status))
g_return_stat = WEXITSTATUS(child_status);
else if (g_rem_ptr->msg.flags == (unsigned)CTRLC_FLAG)
g_return_stat = 0;
else
g_return_stat = 1;
g_sig_chld = 0;
}
/* Alarm handler called after SHELL_TIMEOUT value
* This handler kills the non-responsive shell process
* with the PID value g_shellsync_pid
*/
static void
sigalrm_handler(int s)
{
UNUSED_PARAMETER(s);
if (g_shellsync_pid) {
kill(g_shellsync_pid, SIGINT);
}
#ifdef RWL_SOCKET
g_sig_chld = 0;
#endif
}
static void
def_handler(int s)
{
UNUSED_PARAMETER(s);
kill(g_shellsync_pid, SIGKILL);
exit(0);
}
static void
pipe_handler(int s)
{
UNUSED_PARAMETER(s);
kill(g_shellsync_pid, SIGKILL);
}
int
main(int argc, char **argv)
{
int err = 0;
struct ifreq *ifr;
rwl_server_name = argv[0];
if ((ifr = calloc(1, sizeof(*ifr))) == NULL)
{
DPRINT_ERR(ERR, "wl_server: Unable to allocate memory for handle\n");
exit(1);
}
memset(ifr, 0, sizeof(*ifr));
#ifdef PCIE_MFGTEST
/* For router platform hardcode to eth1 */
strcpy(ifr->ifr_name, "eth1");
#endif /* PCIE_MFGTEST */
/* use default interface */
if (!ifr->ifr_name[0])
wl_find_adapter(ifr);
/* validate the interface */
if (!ifr->ifr_name[0] || wl_check((void *)ifr)) {
DPRINT_INFO(OUTPUT, "wl_server: wl driver adapter not found\n");
}
g_wl_handle = ifr;
/* Register signal handlers */
signal(SIGCHLD, rwl_chld_handler);
signal(SIGALRM, sigalrm_handler);
signal(SIGTERM, def_handler);
signal(SIGPIPE, pipe_handler);
signal(SIGABRT, def_handler);
#ifdef RWL_DONGLE
signal(SIGINT, handle_ctrlc);
#endif
/* Main server process for all transport types */
err = remote_server_exec(argc, argv, ifr);
free(ifr);
return err;
}
/*
* Funtion to store old interface.
*/
void
store_old_interface(void *wl, char *old_intf_name)
{
strcpy(old_intf_name, ((struct ifreq *)wl)->ifr_name);
}
/*
* Function to set interface.
*/
int
set_interface(void *wl, char *intf_name)
{
struct ifreq *ifr = (struct ifreq *)wl;
if (strlen(intf_name) != 0) {
strncpy(ifr->ifr_name, intf_name, strlen(intf_name) + 1);
return BCME_OK;
}
else {
DPRINT_DBG(OUTPUT, "Default Interface will be used ... \n");
return BCME_ERROR;
}
}