project:cfg:BoardConfig_IPC: Added fastboot BoardConfig file and firmware post-scripts, distinguishing between the BoardConfigs for Luckfox Pico Pro and Luckfox Pico Max. project:app: Added fastboot_client and rk_smart_door for quick boot applications; updated rkipc app to adapt to the latest media library. media:samples: Added more usage examples. media:rockit: Fixed bugs; removed support for retrieving data frames from VPSS. media:isp: Updated rkaiq library and related tools to support connection to RKISP_Tuner. sysdrv:Makefile: Added support for compiling drv_ko on Luckfox Pico Ultra W using Ubuntu; added support for custom root filesystem. sysdrv:tools:board: Updated Buildroot optional mirror sources, updated some software versions, and stored device tree files and configuration files that undergo multiple modifications for U-Boot and kernel separately. sysdrv:source:mcu: Used RISC-V MCU SDK with RT-Thread system, mainly for initializing camera AE during quick boot. sysdrv:source:uboot: Added support for fastboot; added high baud rate DDR bin for serial firmware upgrades. sysdrv:source:kernel: Upgraded to version 5.10.160; increased NPU frequency for RV1106G3; added support for fastboot. Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
1617 lines
52 KiB
C
1617 lines
52 KiB
C
/**
|
|
******************************************************************************
|
|
*
|
|
* @file main.c
|
|
*
|
|
* @brief Entry point of the RWNX driver
|
|
*
|
|
* Copyright (C) RivieraWaves 2012-2019
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/inetdevice.h>
|
|
//#include <net/cfg80211.h>
|
|
#include <net/ip.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/netdevice.h>
|
|
#include <net/netlink.h>
|
|
#include <net/genetlink.h>
|
|
#include <linux/wireless.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/random.h>
|
|
#include <linux/string.h>
|
|
#include "rwnx_defs.h"
|
|
//#include "rwnx_dini.h"
|
|
//#include "rwnx_msg_tx.h"
|
|
#include "reg_access.h"
|
|
#include "hal_desc.h"
|
|
#include "rwnx_debugfs.h"
|
|
#include "rwnx_cfgfile.h"
|
|
#include "rwnx_irqs.h"
|
|
//#include "rwnx_radar.h"
|
|
#include "rwnx_version.h"
|
|
#ifdef CONFIG_RWNX_BFMER
|
|
#include "rwnx_bfmer.h"
|
|
#endif //(CONFIG_RWNX_BFMER)
|
|
//#include "rwnx_tdls.h"
|
|
#include "rwnx_events.h"
|
|
#include "rwnx_compat.h"
|
|
#include "rwnx_version.h"
|
|
#include "rwnx_main.h"
|
|
#include "aicwf_txrxif.h"
|
|
#include "aicwf_custom_utils.h"
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
#include "aicwf_sdio.h"
|
|
#endif
|
|
#ifdef AICWF_USB_SUPPORT
|
|
#include "aicwf_usb.h"
|
|
#endif
|
|
|
|
#define RW_DRV_DESCRIPTION "Driver for Linux"
|
|
#define RW_DRV_COPYRIGHT "Copyright(c) 2015-2017 RivieraWaves"
|
|
#define RW_DRV_AUTHOR "RivieraWaves S.A.S"
|
|
|
|
#define RWNX_PRINT_CFM_ERR(req) \
|
|
printk(KERN_CRIT "%s: Status Error(%d)\n", #req, (&req##_cfm)->status)
|
|
|
|
void rwnx_data_dump(char* tag, void* data, unsigned long len){
|
|
unsigned long i = 0;
|
|
char* data_ = (char* )data;
|
|
|
|
printk("%s %s len:(%lu)\r\n", __func__, tag, len);
|
|
|
|
for (i = 0; i < len; i += 16){
|
|
printk("%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
|
|
data_[0 + i],
|
|
data_[1 + i],
|
|
data_[2 + i],
|
|
data_[3 + i],
|
|
data_[4 + i],
|
|
data_[5 + i],
|
|
data_[6 + i],
|
|
data_[7 + i],
|
|
data_[8 + i],
|
|
data_[9 + i],
|
|
data_[10 + i],
|
|
data_[11 + i],
|
|
data_[12 + i],
|
|
data_[13 + i],
|
|
data_[14 + i],
|
|
data_[15 + i]);
|
|
}
|
|
|
|
}
|
|
|
|
#define CMD_MAXARGS 30
|
|
static int parse_line (char *line, char *argv[])
|
|
{
|
|
int nargs = 0;
|
|
|
|
while (nargs < CMD_MAXARGS) {
|
|
/* skip any white space */
|
|
while ((*line == ' ') || (*line == '\t')) {
|
|
++line;
|
|
}
|
|
|
|
if (*line == '\0') { /* end of line, no more args */
|
|
argv[nargs] = 0;
|
|
return (nargs);
|
|
}
|
|
|
|
/* Argument include space should be bracketed by quotation mark */
|
|
if (*line == '\"') {
|
|
/* Skip quotation mark */
|
|
line++;
|
|
|
|
/* Begin of argument string */
|
|
argv[nargs++] = line;
|
|
|
|
/* Until end of argument */
|
|
while(*line && (*line != '\"')) {
|
|
++line;
|
|
}
|
|
} else {
|
|
argv[nargs++] = line; /* begin of argument string */
|
|
|
|
/* find end of string */
|
|
while(*line && (*line != ' ') && (*line != '\t')) {
|
|
++line;
|
|
}
|
|
}
|
|
|
|
if (*line == '\0') { /* end of line, no more args */
|
|
argv[nargs] = 0;
|
|
return (nargs);
|
|
}
|
|
|
|
*line++ = '\0'; /* terminate current arg */
|
|
}
|
|
|
|
printk("** Too many args (max. %d) **\n", CMD_MAXARGS);
|
|
|
|
return (nargs);
|
|
}
|
|
|
|
unsigned int command_strtoul(const char *cp, char **endp, unsigned int base)
|
|
{
|
|
unsigned int result = 0, value, is_neg=0;
|
|
|
|
if (*cp == '0') {
|
|
cp++;
|
|
if ((*cp == 'x') && isxdigit(cp[1])) {
|
|
base = 16;
|
|
cp++;
|
|
}
|
|
if (!base) {
|
|
base = 8;
|
|
}
|
|
}
|
|
if (!base) {
|
|
base = 10;
|
|
}
|
|
if (*cp == '-') {
|
|
is_neg = 1;
|
|
cp++;
|
|
}
|
|
while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp - '0' : (islower(*cp) ? toupper(*cp) : *cp) - 'A' + 10) < base) {
|
|
result = result * base + value;
|
|
cp++;
|
|
}
|
|
if (is_neg)
|
|
result = (unsigned int)((int)result * (-1));
|
|
|
|
if (endp)
|
|
*endp = (char *)cp;
|
|
return result;
|
|
}
|
|
|
|
int handle_private_cmd(struct net_device *net, char *command, u32 cmd_len)
|
|
{
|
|
int bytes_written = 0;
|
|
char* para = NULL;
|
|
char* cmd = NULL;
|
|
char *argv[CMD_MAXARGS + 1];
|
|
int argc;
|
|
#ifdef CONFIG_RFTEST
|
|
struct dbg_rftest_cmd_cfm cfm = {{0,}};
|
|
u8_l mac_addr[6];
|
|
cmd_rf_settx_t settx_param;
|
|
cmd_rf_rx_t setrx_param;
|
|
int freq;
|
|
cmd_rf_getefuse_t getefuse_param;
|
|
cmd_rf_setfreq_t cmd_setfreq;
|
|
u8_l ana_pwr;
|
|
u8_l dig_pwr;
|
|
u8_l pwr;
|
|
u8_l xtal_cap;
|
|
u8_l xtal_cap_fine;
|
|
u8_l vendor_info;
|
|
#ifdef CONFIG_USB_BT
|
|
int bt_index;
|
|
u8_l dh_cmd_reset[4];
|
|
u8_l dh_cmd_txdh[18];
|
|
u8_l dh_cmd_rxdh[17];
|
|
u8_l dh_cmd_stop[5];
|
|
#endif
|
|
#endif
|
|
//u8_l buf[2];
|
|
//s8_l freq_ = 0;
|
|
//u8_l func = 0;
|
|
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
|
|
if ((argc = parse_line(command, argv)) == 0) {
|
|
return -1;
|
|
}
|
|
|
|
do {
|
|
#ifdef CONFIG_RFTEST
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
struct rwnx_hw *p_rwnx_hw = g_rwnx_plat->sdiodev->rwnx_hw;
|
|
#endif
|
|
#ifdef AICWF_USB_SUPPORT
|
|
struct rwnx_hw *p_rwnx_hw = g_rwnx_plat->usbdev->rwnx_hw;
|
|
#endif
|
|
if (strcasecmp(argv[0], "GET_RX_RESULT") ==0) {
|
|
printk("get_rx_result\n");
|
|
rwnx_send_rftest_req(p_rwnx_hw, GET_RX_RESULT, 0, NULL, &cfm);
|
|
memcpy(command, &cfm.rftest_result[0], 8);
|
|
bytes_written = 8;
|
|
} else if (strcasecmp(argv[0], "SET_TX") == 0) {
|
|
printk("set_tx\n");
|
|
if (argc < 6) {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
settx_param.chan = command_strtoul(argv[1], NULL, 10);
|
|
settx_param.bw = command_strtoul(argv[2], NULL, 10);
|
|
settx_param.mode = command_strtoul(argv[3], NULL, 10);
|
|
settx_param.rate = command_strtoul(argv[4], NULL, 10);
|
|
settx_param.length = command_strtoul(argv[5], NULL, 10);
|
|
printk("txparam:%d,%d,%d,%d,%d\n", settx_param.chan, settx_param.bw,
|
|
settx_param.mode, settx_param.rate, settx_param.length);
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_TX, sizeof(cmd_rf_settx_t), (u8_l *)&settx_param, NULL);
|
|
} else if (strcasecmp(argv[0], "SET_TXSTOP") == 0) {
|
|
printk("settx_stop\n");
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_TXSTOP, 0, NULL, NULL);
|
|
} else if (strcasecmp(argv[0], "SET_TXTONE") == 0) {
|
|
printk("set_tx_tone,argc:%d\n",argc);
|
|
if ((argc == 2) || (argc == 3)) {
|
|
printk("argv 1:%s\n",argv[1]);
|
|
//u8_l func = (u8_l)command_strtoul(argv[1], NULL, 16);
|
|
func = (u8_l)command_strtoul(argv[1], NULL, 16);
|
|
//s8_l freq;
|
|
if (argc == 3) {
|
|
printk("argv 2:%s\n",argv[2]);
|
|
freq_ = (u8_l)command_strtoul(argv[2], NULL, 10);
|
|
} else {
|
|
freq_ = 0;
|
|
};
|
|
//u8_l buf[2] = {func, (u8_l)freq};
|
|
buf[0] = func;
|
|
buf[1] = (u8_l)freq_;
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_TXTONE, argc - 1, buf, NULL);
|
|
} else {
|
|
printk("wrong args\n");
|
|
}
|
|
} else if (strcasecmp(argv[0], "SET_RX") == 0) {
|
|
printk("set_rx\n");
|
|
if (argc < 3) {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
setrx_param.chan = command_strtoul(argv[1], NULL, 10);
|
|
setrx_param.bw = command_strtoul(argv[2], NULL, 10);
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_RX, sizeof(cmd_rf_rx_t), (u8_l *)&setrx_param, NULL);
|
|
} else if (strcasecmp(argv[0], "SET_RXSTOP") == 0) {
|
|
printk("set_rxstop\n");
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_RXSTOP, 0, NULL, NULL);
|
|
} else if (strcasecmp(argv[0], "SET_RX_METER") == 0) {
|
|
printk("set_rx_meter\n");
|
|
freq = (int)command_strtoul(argv[1], NULL, 10);
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_RX_METER, sizeof(freq), (u8_l *)&freq, NULL);
|
|
} else if (strcasecmp(argv[0], "SET_FREQ_CAL") == 0) {
|
|
printk("set_freq_cal\n");
|
|
if (argc < 2) {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
cmd_setfreq.val = command_strtoul(argv[1], NULL, 16);
|
|
printk("param:%x\r\n", cmd_setfreq.val);
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_FREQ_CAL, sizeof(cmd_rf_setfreq_t), (u8_l *)&cmd_setfreq, &cfm);
|
|
memcpy(command, &cfm.rftest_result[0], 4);
|
|
bytes_written = 4;
|
|
} else if (strcasecmp(argv[0], "SET_FREQ_CAL_FINE") == 0) {
|
|
printk("set_freq_cal_fine\n");
|
|
if (argc < 2) {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
cmd_setfreq.val = command_strtoul(argv[1], NULL, 16);
|
|
printk("param:%x\r\n", cmd_setfreq.val);
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_FREQ_CAL_FINE, sizeof(cmd_rf_setfreq_t), (u8_l *)&cmd_setfreq, &cfm);
|
|
memcpy(command, &cfm.rftest_result[0], 4);
|
|
bytes_written = 4;
|
|
} else if (strcasecmp(argv[0], "GET_EFUSE_BLOCK") == 0) {
|
|
printk("get_efuse_block\n");
|
|
if (argc < 2) {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
getefuse_param.block = command_strtoul(argv[1], NULL, 10);
|
|
rwnx_send_rftest_req(p_rwnx_hw, GET_EFUSE_BLOCK, sizeof(cmd_rf_getefuse_t), (u8_l *)&getefuse_param, &cfm);
|
|
printk("get val=%x\r\n", cfm.rftest_result[0]);
|
|
memcpy(command, &cfm.rftest_result[0], 4);
|
|
bytes_written = 4;
|
|
} else if (strcasecmp(argv[0], "SET_POWER") == 0) {
|
|
printk("set_power\n");
|
|
ana_pwr = command_strtoul(argv[1], NULL, 16);
|
|
dig_pwr = command_strtoul(argv[2], NULL, 16);
|
|
pwr = (ana_pwr << 4 | dig_pwr);
|
|
if (ana_pwr > 0xf || dig_pwr > 0xf) {
|
|
printk("invalid param\r\n");
|
|
break;
|
|
}
|
|
printk("pwr =%x\r\n", pwr);
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_POWER, sizeof(pwr), (u8_l *)&pwr, NULL);
|
|
} else if (strcasecmp(argv[0], "SET_XTAL_CAP")==0) {
|
|
printk("set_xtal_cap\n");
|
|
if (argc < 2) {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
xtal_cap = command_strtoul(argv[1], NULL, 10);
|
|
printk("xtal_cap =%x\r\n", xtal_cap);
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_XTAL_CAP, sizeof(xtal_cap), (u8_l *)&xtal_cap, &cfm);
|
|
memcpy(command, &cfm.rftest_result[0], 4);
|
|
bytes_written = 4;
|
|
} else if (strcasecmp(argv[0], "SET_XTAL_CAP_FINE")==0) {
|
|
printk("set_xtal_cap_fine\n");
|
|
if (argc < 2) {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
xtal_cap_fine = command_strtoul(argv[1], NULL, 10);
|
|
printk("xtal_cap_fine =%x\r\n", xtal_cap_fine);
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_XTAL_CAP_FINE, sizeof(xtal_cap_fine), (u8_l *)&xtal_cap_fine, &cfm);
|
|
memcpy(command, &cfm.rftest_result[0], 4);
|
|
bytes_written = 4;
|
|
} else if (strcasecmp(argv[0], "SET_MAC_ADDR")==0) {
|
|
printk("set_mac_addr\n");
|
|
if (argc < 7) {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
mac_addr[5] = command_strtoul(argv[1], NULL, 16);
|
|
mac_addr[4] = command_strtoul(argv[2], NULL, 16);
|
|
mac_addr[3] = command_strtoul(argv[3], NULL, 16);
|
|
mac_addr[2] = command_strtoul(argv[4], NULL, 16);
|
|
mac_addr[1] = command_strtoul(argv[5], NULL, 16);
|
|
mac_addr[0] = command_strtoul(argv[6], NULL, 16);
|
|
printk("set macaddr:%x,%x,%x,%x,%x,%x\n", mac_addr[5], mac_addr[4], mac_addr[3], mac_addr[2], mac_addr[1], mac_addr[0]);
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_MAC_ADDR, sizeof(mac_addr), (u8_l *)&mac_addr, NULL);
|
|
} else if (strcasecmp(argv[0], "GET_MAC_ADDR")==0) {
|
|
printk("get mac addr\n");
|
|
rwnx_send_rftest_req(p_rwnx_hw, GET_MAC_ADDR, 0, NULL, &cfm);
|
|
memcpy(command, &cfm.rftest_result[0], 8);
|
|
bytes_written = 8;
|
|
printk("0x%x,0x%x\n", cfm.rftest_result[0], cfm.rftest_result[1]);
|
|
} else if (strcasecmp(argv[0], "SET_BT_MAC_ADDR") == 0) {
|
|
printk("set_bt_mac_addr\n");
|
|
if (argc < 7) {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
mac_addr[5] = command_strtoul(argv[1], NULL, 16);
|
|
mac_addr[4] = command_strtoul(argv[2], NULL, 16);
|
|
mac_addr[3] = command_strtoul(argv[3], NULL, 16);
|
|
mac_addr[2] = command_strtoul(argv[4], NULL, 16);
|
|
mac_addr[1] = command_strtoul(argv[5], NULL, 16);
|
|
mac_addr[0] = command_strtoul(argv[6], NULL, 16);
|
|
printk("set bt macaddr:%x,%x,%x,%x,%x,%x\n", mac_addr[5], mac_addr[4], mac_addr[3], mac_addr[2], mac_addr[1], mac_addr[0]);
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_BT_MAC_ADDR, sizeof(mac_addr), (u8_l *)&mac_addr, NULL);
|
|
} else if (strcasecmp(argv[0], "GET_BT_MAC_ADDR")==0) {
|
|
printk("get bt mac addr\n");
|
|
rwnx_send_rftest_req(p_rwnx_hw, GET_BT_MAC_ADDR, 0, NULL, &cfm);
|
|
memcpy(command, &cfm.rftest_result[0], 8);
|
|
bytes_written = 8;
|
|
printk("0x%x,0x%x\n", cfm.rftest_result[0], cfm.rftest_result[1]);
|
|
} else if (strcasecmp(argv[0], "SET_VENDOR_INFO")==0) {
|
|
vendor_info = command_strtoul(argv[1], NULL, 16);
|
|
printk("set vendor info:%x\n", vendor_info);
|
|
rwnx_send_rftest_req(p_rwnx_hw, SET_VENDOR_INFO, 1, &vendor_info, &cfm);
|
|
memcpy(command, &cfm.rftest_result[0], 1);
|
|
bytes_written = 1;
|
|
printk("0x%x\n", cfm.rftest_result[0]);
|
|
} else if (strcasecmp(argv[0], "GET_VENDOR_INFO")==0) {
|
|
printk("get vendor info\n");
|
|
rwnx_send_rftest_req(p_rwnx_hw, GET_VENDOR_INFO, 0, NULL, &cfm);
|
|
memcpy(command, &cfm.rftest_result[0], 1);
|
|
bytes_written = 1;
|
|
printk("0x%x\n", cfm.rftest_result[0]);
|
|
} else if (strcasecmp(argv[0], "GET_FREQ_CAL") == 0) {
|
|
printk("get freq cal\n");
|
|
rwnx_send_rftest_req(p_rwnx_hw, GET_FREQ_CAL, 0, NULL, &cfm);
|
|
memcpy(command, &cfm.rftest_result[0], 4);
|
|
bytes_written = 4;
|
|
printk("cap=0x%x, cap_fine=0x%x\n", cfm.rftest_result[0] & 0x0000ffff, (cfm.rftest_result[0] >> 16) & 0x0000ffff);
|
|
} else if (strcasecmp(argv[0], "RDWR_PWRMM") == 0) {
|
|
printk("read/write txpwr manul mode\n");
|
|
if (argc <= 1) { // read cur
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRMM, 0, NULL, &cfm);
|
|
} else { // write
|
|
u8_l pwrmm = (u8_l)command_strtoul(argv[1], NULL, 16);
|
|
pwrmm = (pwrmm) ? 1 : 0;
|
|
printk("set pwrmm = %x\r\n", pwrmm);
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRMM, sizeof(pwrmm), (u8_l *)&pwrmm, &cfm);
|
|
}
|
|
memcpy(command, &cfm.rftest_result[0], 4);
|
|
bytes_written = 4;
|
|
} else if (strcasecmp(argv[0], "RDWR_PWRIDX") == 0) {
|
|
u8_l func = 0;
|
|
printk("read/write txpwr index\n");
|
|
if (argc > 1) {
|
|
func = (u8_l)command_strtoul(argv[1], NULL, 16);
|
|
}
|
|
if (func == 0) { // read cur
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRIDX, 0, NULL, &cfm);
|
|
} else if (func <= 2) { // write 2.4g/5g pwr idx
|
|
if (argc > 3) {
|
|
u8_l type = (u8_l)command_strtoul(argv[2], NULL, 16);
|
|
u8_l pwridx = (u8_l)command_strtoul(argv[3], NULL, 10);
|
|
u8_l buf[3] = {func, type, pwridx};
|
|
printk("set pwridx:[%x][%x]=%x\r\n", func, type, pwridx);
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRIDX, sizeof(buf), buf, &cfm);
|
|
} else {
|
|
printk("wrong args\n");
|
|
}
|
|
} else {
|
|
printk("wrong func: %x\n", func);
|
|
}
|
|
memcpy(command, &cfm.rftest_result[0], 7);
|
|
bytes_written = 7;
|
|
} else if (strcasecmp(argv[0], "RDWR_PWROFST") == 0) {
|
|
u8_l func = 0;
|
|
printk("read/write txpwr offset\n");
|
|
if (argc > 1) {
|
|
func = (u8_l)command_strtoul(argv[1], NULL, 16);
|
|
}
|
|
if (func == 0) { // read cur
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWROFST, 0, NULL, &cfm);
|
|
} else if (func <= 2) { // write 2.4g/5g pwr ofst
|
|
if (argc > 3) {
|
|
u8_l chgrp = (u8_l)command_strtoul(argv[2], NULL, 16);
|
|
s8_l pwrofst = (u8_l)command_strtoul(argv[3], NULL, 10);
|
|
u8_l buf[3] = {func, chgrp, (u8_l)pwrofst};
|
|
printk("set pwrofst:[%x][%x]=%d\r\n", func, chgrp, pwrofst);
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWROFST, sizeof(buf), buf, &cfm);
|
|
} else {
|
|
printk("wrong args\n");
|
|
}
|
|
} else {
|
|
printk("wrong func: %x\n", func);
|
|
}
|
|
memcpy(command, &cfm.rftest_result[0], 7);
|
|
bytes_written = 7;
|
|
} else if (strcasecmp(argv[0], "RDWR_DRVIBIT") == 0) {
|
|
u8_l func = 0;
|
|
printk("read/write pa drv_ibit\n");
|
|
if (argc > 1) {
|
|
func = (u8_l)command_strtoul(argv[1], NULL, 16);
|
|
}
|
|
if (func == 0) { // read cur
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_DRVIBIT, 0, NULL, &cfm);
|
|
} else if (func == 1) { // write 2.4g pa drv_ibit
|
|
if (argc > 2) {
|
|
u8_l ibit = (u8_l)command_strtoul(argv[2], NULL, 16);
|
|
u8_l buf[2] = {func, ibit};
|
|
printk("set drvibit:[%x]=%x\r\n", func, ibit);
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_DRVIBIT, sizeof(buf), buf, &cfm);
|
|
} else {
|
|
printk("wrong args\n");
|
|
}
|
|
} else {
|
|
printk("wrong func: %x\n", func);
|
|
}
|
|
memcpy(command, &cfm.rftest_result[0], 16);
|
|
bytes_written = 16;
|
|
} else if (strcasecmp(argv[0], "RDWR_EFUSE_PWROFST") == 0) {
|
|
u8_l func = 0;
|
|
printk("read/write txpwr offset into efuse\n");
|
|
if (argc > 1) {
|
|
func = (u8_l)command_strtoul(argv[1], NULL, 16);
|
|
}
|
|
if (func == 0) { // read cur
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_PWROFST, 0, NULL, &cfm);
|
|
} else if (func <= 2) { // write 2.4g/5g pwr ofst
|
|
if (argc > 3) {
|
|
u8_l chgrp = (u8_l)command_strtoul(argv[2], NULL, 16);
|
|
s8_l pwrofst = (u8_l)command_strtoul(argv[3], NULL, 10);
|
|
u8_l buf[3] = {func, chgrp, (u8_l)pwrofst};
|
|
printk("set efuse pwrofst:[%x][%x]=%d\r\n", func, chgrp, pwrofst);
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_PWROFST, sizeof(buf), buf, &cfm);
|
|
} else {
|
|
printk("wrong args\n");
|
|
}
|
|
} else {
|
|
printk("wrong func: %x\n", func);
|
|
}
|
|
memcpy(command, &cfm.rftest_result[0], 7);
|
|
bytes_written = 7;
|
|
} else if (strcasecmp(argv[0], "RDWR_EFUSE_DRVIBIT") == 0) {
|
|
u8_l func = 0;
|
|
printk("read/write pa drv_ibit into efuse\n");
|
|
if (argc > 1) {
|
|
func = (u8_l)command_strtoul(argv[1], NULL, 16);
|
|
}
|
|
if (func == 0) { // read cur
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_DRVIBIT, 0, NULL, &cfm);
|
|
} else if (func == 1) { // write 2.4g pa drv_ibit
|
|
if (argc > 2) {
|
|
u8_l ibit = (u8_l)command_strtoul(argv[2], NULL, 16);
|
|
u8_l buf[2] = {func, ibit};
|
|
printk("set efuse drvibit:[%x]=%x\r\n", func, ibit);
|
|
rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_DRVIBIT, sizeof(buf), buf, &cfm);
|
|
} else {
|
|
printk("wrong args\n");
|
|
}
|
|
} else {
|
|
printk("wrong func: %x\n", func);
|
|
}
|
|
memcpy(command, &cfm.rftest_result[0], 4);
|
|
bytes_written = 4;
|
|
}
|
|
#ifdef CONFIG_USB_BT
|
|
else if (strcasecmp(argv[0], "BT_RESET") == 0) {
|
|
if (argc == 5) {
|
|
printk("btrf reset\n");
|
|
for (bt_index = 0; bt_index < 4; bt_index++) {
|
|
dh_cmd_reset[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
|
|
printk("0x%x ",dh_cmd_reset[bt_index]);
|
|
}
|
|
printk("\n");
|
|
} else {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
rwnx_send_rftest_req(p_rwnx_hw, BT_RESET, sizeof(dh_cmd_reset), (u8_l *)&dh_cmd_reset, NULL);
|
|
} else if (strcasecmp(argv[0], "BT_TXDH") == 0) {
|
|
if (argc == 19) {
|
|
printk("btrf txdh\n");
|
|
for (bt_index = 0; bt_index < 18; bt_index++) {
|
|
dh_cmd_txdh[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
|
|
printk("0x%x ", dh_cmd_txdh[bt_index]);
|
|
}
|
|
printk("\n");
|
|
} else {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
rwnx_send_rftest_req(p_rwnx_hw, BT_TXDH, sizeof(dh_cmd_txdh), (u8_l *)&dh_cmd_txdh, NULL);
|
|
} else if (strcasecmp(argv[0], "BT_RXDH") == 0) {
|
|
if (argc == 18) {
|
|
printk("btrf rxdh\n");
|
|
for (bt_index = 0; bt_index < 17; bt_index++) {
|
|
dh_cmd_rxdh[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
|
|
printk("0x%x ", dh_cmd_rxdh[bt_index]);
|
|
}
|
|
printk("\n");
|
|
} else {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
rwnx_send_rftest_req(p_rwnx_hw, BT_RXDH, sizeof(dh_cmd_rxdh), (u8_l *)&dh_cmd_rxdh, NULL);
|
|
} else if (strcasecmp(argv[0], "BT_STOP") == 0) {
|
|
if (argc == 6) {
|
|
printk("btrf stop\n");
|
|
for (bt_index = 0; bt_index < 5; bt_index++) {
|
|
dh_cmd_stop[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
|
|
printk("0x%x ", dh_cmd_stop[bt_index]);
|
|
}
|
|
printk("\n");
|
|
} else {
|
|
printk("wrong param\n");
|
|
break;
|
|
}
|
|
rwnx_send_rftest_req(p_rwnx_hw, BT_STOP, sizeof(dh_cmd_stop), (u8_l *)&dh_cmd_stop, NULL);
|
|
}
|
|
#endif
|
|
else {
|
|
printk("wrong cmd:%s in %s\n", cmd, __func__);
|
|
}
|
|
#endif
|
|
} while(0);
|
|
kfree(cmd);
|
|
kfree(para);
|
|
return bytes_written;
|
|
}
|
|
|
|
int android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
|
|
{
|
|
#define PRIVATE_COMMAND_MAX_LEN 8192
|
|
#define PRIVATE_COMMAND_DEF_LEN 4096
|
|
int ret = 0;
|
|
char *command = NULL;
|
|
int bytes_written = 0;
|
|
android_wifi_priv_cmd priv_cmd;
|
|
int buf_size = 0;
|
|
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
|
|
///todo: add our lock
|
|
//net_os_wake_lock(net);
|
|
|
|
|
|
/*if (!capable(CAP_NET_ADMIN)) {
|
|
ret = -EPERM;
|
|
goto exit;
|
|
}*/
|
|
if (!ifr->ifr_data) {
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0))
|
|
if (in_compat_syscall())
|
|
#else
|
|
if (is_compat_task())
|
|
#endif
|
|
{
|
|
compat_android_wifi_priv_cmd compat_priv_cmd;
|
|
if (copy_from_user(&compat_priv_cmd, ifr->ifr_data, sizeof(compat_android_wifi_priv_cmd))) {
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
priv_cmd.buf = compat_ptr(compat_priv_cmd.buf);
|
|
priv_cmd.used_len = compat_priv_cmd.used_len;
|
|
priv_cmd.total_len = compat_priv_cmd.total_len;
|
|
} else
|
|
#endif /* CONFIG_COMPAT */
|
|
{
|
|
if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) {
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
}
|
|
if ((priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) || (priv_cmd.total_len < 0)) {
|
|
printk("%s: buf length invalid:%d\n", __FUNCTION__, priv_cmd.total_len);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
buf_size = max(priv_cmd.total_len, PRIVATE_COMMAND_DEF_LEN);
|
|
command = kmalloc((buf_size + 1), GFP_KERNEL);
|
|
|
|
if (!command)
|
|
{
|
|
printk("%s: failed to allocate memory\n", __FUNCTION__);
|
|
ret = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) {
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
command[priv_cmd.total_len] = '\0';
|
|
|
|
/* outputs */
|
|
printk("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name);
|
|
printk("cmd = %d\n", cmd);
|
|
printk("buf_size=%d\n", buf_size);
|
|
|
|
|
|
bytes_written = handle_private_cmd(net, command, priv_cmd.total_len);
|
|
if (bytes_written >= 0) {
|
|
if ((bytes_written == 0) && (priv_cmd.total_len > 0)) {
|
|
command[0] = '\0';
|
|
}
|
|
if (bytes_written >= priv_cmd.total_len) {
|
|
printk("%s: err. bytes_written:%d >= buf_size:%d \n",
|
|
__FUNCTION__, bytes_written, buf_size);
|
|
goto exit;
|
|
}
|
|
bytes_written++;
|
|
priv_cmd.used_len = bytes_written;
|
|
if (copy_to_user(priv_cmd.buf, command, bytes_written)) {
|
|
printk("%s: failed to copy data to user buffer\n", __FUNCTION__);
|
|
ret = -EFAULT;
|
|
}
|
|
}
|
|
else {
|
|
/* Propagate the error */
|
|
ret = bytes_written;
|
|
}
|
|
|
|
exit:
|
|
///todo: add our unlock
|
|
//net_os_wake_unlock(net);
|
|
kfree(command);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_VNET_MODE
|
|
int custom_msg_cmd_parse(char *command, u32 cmd_len, u32 *argc, char buf[][APP_CMD_BUF_MAX])
|
|
{
|
|
int i = 0, len = 0, ret = 0, val = 0;
|
|
char *cmd_cpy = NULL, *token = NULL;
|
|
|
|
cmd_cpy = kzalloc((cmd_len+1), GFP_KERNEL);
|
|
memcpy(cmd_cpy, command, cmd_len);
|
|
cmd_cpy[cmd_len] = '\0';
|
|
|
|
token = strsep(&cmd_cpy, " ");
|
|
while(token && i<APP_CMD_NUM_MAX) {
|
|
len = strlen(token);
|
|
if(len > APP_CMD_BUF_MAX) {
|
|
kfree(cmd_cpy);
|
|
return 0;
|
|
}
|
|
|
|
strncpy(buf[i], token, len);
|
|
buf[i++][len] = '\0';
|
|
token = strsep(&cmd_cpy, " ");
|
|
}
|
|
*argc = i;
|
|
kfree(cmd_cpy);
|
|
|
|
// application insure that command is not null, so buf[0] is not null.
|
|
ret = kstrtouint(buf[0], 10, &val);
|
|
if(!ret)
|
|
return val;
|
|
else
|
|
// str2uint process falid.
|
|
return ret;
|
|
}
|
|
|
|
extern struct rwnx_aic_chardev chardev;
|
|
int handle_custom_msg(struct net_device *net, char *command, u32 cmd_len)
|
|
{
|
|
//struct dbg_custom_msg_cfm *cust_msg_cfm;
|
|
//printk("cmd: %s, %ld\n",command, strlen(command));
|
|
|
|
int ret = 0;
|
|
u32 argc = 0;
|
|
aicwf_custom_msg_app_cmd cust_app_cmd;
|
|
|
|
char (*buf)[APP_CMD_BUF_MAX] = (char (*)[APP_CMD_BUF_MAX])kzalloc(APP_CMD_NUM_MAX*APP_CMD_BUF_MAX, GFP_KERNEL);
|
|
if(!buf) {
|
|
printk("%s: alloc buf fail\n", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
ret = custom_msg_cmd_parse(command, cmd_len, &argc, buf);
|
|
if(!ret) {
|
|
printk("Please input valid args: custom_msg vnet0 [mode] <arg1> <arg2> <arg3>\n");
|
|
kfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
switch(ret) {
|
|
case APP_CMD_CONNECT:
|
|
printk("APP_CMD_CONNECT\n");
|
|
if(argc != 3) {
|
|
printk("Please input valid args: custom_msg vnet0 1 ssid password\n");
|
|
break;
|
|
}
|
|
if(strlen(buf[1]) >= AP_SSID_BUF_MAX || strlen(buf[2]) >= AP_PSWD_BUF_MAX) {
|
|
printk("ssid or password is too long.\n");
|
|
break;
|
|
}
|
|
cust_app_cmd.connect_req.hdr.cmd_id = CUST_CMD_CONNECT_REQ;
|
|
strncpy(cust_app_cmd.connect_req.ssid, buf[1], strlen(buf[1]));
|
|
strncpy(cust_app_cmd.connect_req.pw, buf[2], strlen(buf[2]));
|
|
cust_app_cmd.connect_req.ssid[strlen(buf[1])] = '\0';
|
|
cust_app_cmd.connect_req.pw[strlen(buf[2])] = '\0';
|
|
rwnx_tx_msg((u8 *)&cust_app_cmd.connect_req, sizeof(cust_app_cmd.connect_req));
|
|
break;
|
|
case APP_CMD_DISCONNECT:
|
|
printk("APP_CMD_DISCONNECT\n");
|
|
cust_app_cmd.common_req.cmd_id = CUST_CMD_DISCONNECT_REQ;
|
|
rwnx_tx_msg((u8 *)&cust_app_cmd.common_req, sizeof(cust_app_cmd.common_req));
|
|
break;
|
|
case APP_CMD_ENTER_SLEEP:
|
|
printk("APP_CMD_ENTER_SLEEP\n");
|
|
netif_tx_stop_all_queues(net);
|
|
printk("VNET_DEV: Stop send data-pkg\n");
|
|
cust_app_cmd.common_req.cmd_id = CUST_CMD_ENTER_SLEEP_REQ;
|
|
rwnx_tx_msg((u8 *)&cust_app_cmd.common_req, sizeof(cust_app_cmd.common_req));
|
|
break;
|
|
case APP_CMD_EXIT_SLEEP:
|
|
printk("APP_CMD_EXIT_SLEEP\n");
|
|
cust_app_cmd.common_req.cmd_id = CUST_CMD_EXIT_SLEEP_REQ;
|
|
rwnx_tx_msg((u8 *)&cust_app_cmd.common_req, sizeof(cust_app_cmd.common_req));
|
|
break;
|
|
case APP_CMD_GET_MAC_ADDR:
|
|
printk("APP_CMD_GET_MAC_ADDR\n");
|
|
cust_app_cmd.common_req.cmd_id = CUST_CMD_GET_MAC_ADDR_REQ;
|
|
rwnx_tx_msg((u8 *)&cust_app_cmd.common_req, sizeof(cust_app_cmd.common_req));
|
|
break;
|
|
case APP_CMD_GET_WLAN_STATUS:
|
|
printk("APP_CMD_GET_WLAN_STATUS\n");
|
|
cust_app_cmd.common_req.cmd_id = CUST_CMD_GET_WLAN_STATUS_REQ;
|
|
rwnx_tx_msg((u8 *)&cust_app_cmd.common_req, sizeof(cust_app_cmd.common_req));
|
|
break;
|
|
case APP_CMD_START_AP:
|
|
printk("APP_CMD_START_AP\n");
|
|
if(argc != 4) {
|
|
printk("Please input valid args: custom_msg vnet0 7 ssid password band\n");
|
|
break;
|
|
}
|
|
if(strlen(buf[1]) >= AP_SSID_BUF_MAX || strlen(buf[2]) >= AP_PSWD_BUF_MAX) {
|
|
printk("String ssid or password is too long.\n");
|
|
break;
|
|
}
|
|
cust_app_cmd.ap_req.hdr.cmd_id = CUST_CMD_START_AP_REQ;
|
|
strncpy(cust_app_cmd.ap_req.ssid, buf[1], strlen(buf[1]));
|
|
strncpy(cust_app_cmd.ap_req.pw, buf[2], strlen(buf[2]));
|
|
cust_app_cmd.ap_req.ssid[strlen(buf[1])] = '\0';
|
|
cust_app_cmd.ap_req.pw[strlen(buf[2])] = '\0';
|
|
if(!strncasecmp(buf[3], "2.4G", 4))
|
|
cust_app_cmd.ap_req.band = 0;
|
|
else if(!strncasecmp(buf[3], "5G", 2))
|
|
cust_app_cmd.ap_req.band = 1;
|
|
else
|
|
cust_app_cmd.ap_req.band = 0;
|
|
rwnx_tx_msg((u8 *)&cust_app_cmd.ap_req, sizeof(cust_app_cmd.ap_req));
|
|
break;
|
|
case APP_CMD_CHANGE_AP_MODE:
|
|
printk("APP_CMD_CHANGE_AP_MODE\n");
|
|
cust_app_cmd.common_req.cmd_id = CUST_CMD_CHANGE_AP_MODE_REQ;
|
|
rwnx_tx_msg((u8 *)&cust_app_cmd.common_req, sizeof(cust_app_cmd.common_req));
|
|
break;
|
|
case APP_CMD_STOP_AP:
|
|
printk("APP_CMD_STOP_AP\n");
|
|
cust_app_cmd.common_req.cmd_id = CUST_CMD_STOP_AP_REQ;
|
|
rwnx_tx_msg((u8 *)&cust_app_cmd.common_req, sizeof(cust_app_cmd.common_req));
|
|
break;
|
|
case APP_CMD_SCAN_WIFI:
|
|
printk("APP_CMD_SCAN_WIFI\n");
|
|
cust_app_cmd.common_req.cmd_id = CUST_CMD_SCAN_WIFI_REQ;
|
|
rwnx_tx_msg((u8 *)&cust_app_cmd.common_req, sizeof(cust_app_cmd.common_req));
|
|
break;
|
|
case APP_CMD_HOST_OTA:
|
|
printk("APP_CMD_HOST_OTA\n");
|
|
if(argc != 2) {
|
|
printk("Please input valid args: custom_msg vnet0 10 /your-filepath/update.bin\n");
|
|
break;
|
|
}
|
|
if(strlen(buf[1]) >= APP_CMD_BUF_MAX) {
|
|
printk("String file_path is too long.\n");
|
|
break;
|
|
}
|
|
memcpy(cust_app_cmd.file_path, buf[1], strlen(buf[1]));
|
|
cust_app_cmd.file_path[strlen(buf[1])] = '\0';
|
|
host_ota_test(cust_app_cmd.file_path);
|
|
break;
|
|
}
|
|
kfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
int mcu_cust_msg(struct net_device *net, struct ifreq *ifr, int cmd)
|
|
{
|
|
#ifdef PRIVATE_COMMAND_MAX_LEN
|
|
#undef PRIVATE_COMMAND_MAX_LEN
|
|
#undef PRIVATE_COMMAND_DEF_LEN
|
|
#define PRIVATE_COMMAND_MAX_LEN 8192
|
|
#define PRIVATE_COMMAND_DEF_LEN 4096
|
|
#endif
|
|
int ret = 0;
|
|
char *command = NULL;
|
|
int bytes_written = 0;
|
|
android_wifi_priv_cmd priv_cmd;
|
|
int buf_size = 0;
|
|
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
|
|
///todo: add our lock
|
|
//net_os_wake_lock(net);
|
|
|
|
/*if (!capable(CAP_NET_ADMIN)) {
|
|
ret = -EPERM;
|
|
goto exit;
|
|
}*/
|
|
if (!ifr->ifr_data) {
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0))
|
|
if (in_compat_syscall())
|
|
#else
|
|
if (is_compat_task())
|
|
#endif
|
|
{
|
|
compat_android_wifi_priv_cmd compat_priv_cmd;
|
|
if (copy_from_user(&compat_priv_cmd, ifr->ifr_data, sizeof(compat_android_wifi_priv_cmd))) {
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
priv_cmd.buf = compat_ptr(compat_priv_cmd.buf);
|
|
priv_cmd.used_len = compat_priv_cmd.used_len;
|
|
priv_cmd.total_len = compat_priv_cmd.total_len;
|
|
} else
|
|
#endif /* CONFIG_COMPAT */
|
|
{
|
|
if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) {
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
}
|
|
if ((priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) || (priv_cmd.total_len < 0)) {
|
|
printk("%s: buf length invalid:%d\n", __FUNCTION__, priv_cmd.total_len);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
buf_size = max(priv_cmd.total_len, PRIVATE_COMMAND_DEF_LEN);
|
|
command = kmalloc((buf_size + 1), GFP_KERNEL);
|
|
|
|
if (!command)
|
|
{
|
|
printk("%s: failed to allocate memory\n", __FUNCTION__);
|
|
ret = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
if (copy_from_user(command, priv_cmd.buf, priv_cmd.used_len)) {
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
command[priv_cmd.used_len] = '\0';
|
|
|
|
/* outputs */
|
|
printk("%s: Devipc custom msg \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name);
|
|
//printk("cmd = %x\n", cmd);
|
|
//printk("buf_size=%d\n", buf_size);
|
|
|
|
|
|
bytes_written = handle_custom_msg(net, command, priv_cmd.used_len);
|
|
if (bytes_written >= 0) {
|
|
if ((bytes_written == 0) && (priv_cmd.total_len > 0)) {
|
|
command[0] = '\0';
|
|
}
|
|
if (bytes_written >= priv_cmd.total_len) {
|
|
printk("%s: err. bytes_written:%d >= buf_size:%d \n",
|
|
__FUNCTION__, bytes_written, buf_size);
|
|
goto exit;
|
|
}
|
|
bytes_written++;
|
|
priv_cmd.used_len = bytes_written;
|
|
if (copy_to_user(priv_cmd.buf, command, bytes_written)) {
|
|
printk("%s: failed to copy data to user buffer\n", __FUNCTION__);
|
|
ret = -EFAULT;
|
|
}
|
|
}
|
|
else {
|
|
/* Propagate the error */
|
|
ret = bytes_written;
|
|
}
|
|
|
|
exit:
|
|
///todo: add our unlock
|
|
//net_os_wake_unlock(net);
|
|
if (command)
|
|
kfree(command);
|
|
return ret;
|
|
}
|
|
|
|
static int rwnx_do_ioctl(struct net_device *net, struct ifreq *req, int cmd)
|
|
{
|
|
int ret = 0;
|
|
printk(" %s cmd %d\n", __func__, cmd);
|
|
switch(cmd)
|
|
{
|
|
case IOCTL_HOSTAPD:
|
|
printk("IOCTL_HOSTAPD\n");
|
|
break;
|
|
case IOCTL_WPAS:
|
|
printk("IOCTL_WPAS\n");
|
|
break;
|
|
case SIOCDEVPRIVATE:
|
|
printk("IOCTL SIOCDEVPRIVATE\n");
|
|
break;
|
|
case (SIOCDEVPRIVATE+1):
|
|
printk("IOCTL PRIVATE\n");
|
|
android_priv_cmd(net, req, cmd);
|
|
break;
|
|
case (SIOCDEVPRIVATE+2):
|
|
printk("IOCTL PRIVATE+2\n");
|
|
mcu_cust_msg(net, req, cmd);
|
|
break;
|
|
default:
|
|
ret = -EOPNOTSUPP;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#if 0
|
|
/**
|
|
* struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
|
|
* Called when a user wants to get the network device usage
|
|
* statistics. Drivers must do one of the following:
|
|
* 1. Define @ndo_get_stats64 to fill in a zero-initialised
|
|
* rtnl_link_stats64 structure passed by the caller.
|
|
* 2. Define @ndo_get_stats to update a net_device_stats structure
|
|
* (which should normally be dev->stats) and return a pointer to
|
|
* it. The structure may be changed asynchronously only if each
|
|
* field is written atomically.
|
|
* 3. Update dev->stats asynchronously and atomically, and define
|
|
* neither operation.
|
|
*/
|
|
static struct net_device_stats *rwnx_get_stats(struct net_device *dev)
|
|
{
|
|
struct rwnx_vif *vif = netdev_priv(dev);
|
|
|
|
return &vif->net_stats;
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
* netdev callbacks
|
|
********************************************************************/
|
|
/**
|
|
* int (*ndo_open)(struct net_device *dev);
|
|
* This function is called when network device transistions to the up
|
|
* state.
|
|
*
|
|
* - Start FW if this is the first interface opened
|
|
* - Add interface at fw level
|
|
*/
|
|
extern aicwf_custom_msg_vnet g_custom_msg_vnet;
|
|
static int rwnx_open(struct net_device *dev)
|
|
{
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
//netif_carrier_off(dev);
|
|
netif_tx_start_all_queues(dev);
|
|
if (!netif_carrier_ok(dev))
|
|
netif_carrier_on(dev);
|
|
|
|
if(g_custom_msg_vnet.wlan_status == WLAN_DISCONNECT &&
|
|
g_custom_msg_vnet.ap_status == AIC_AP_CLOSE) {
|
|
// Don`t allow linux-app to send data-pkg by vnet_dev.
|
|
netif_tx_stop_all_queues(dev);
|
|
printk("VNET_DEV: Stop send data-pkg\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* int (*ndo_stop)(struct net_device *dev);
|
|
* This function is called when network device transistions to the down
|
|
* state.
|
|
*
|
|
* - Remove interface at fw level
|
|
* - Reset FW if this is the last interface opened
|
|
*/
|
|
static int rwnx_close(struct net_device *dev)
|
|
{
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
|
|
netdev_info(dev, "CLOSE");
|
|
|
|
if (netif_carrier_ok(dev)) {
|
|
netif_tx_stop_all_queues(dev);
|
|
netif_carrier_off(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
|
|
* This function is called when the Media Access Control address
|
|
* needs to be changed. If this interface is not defined, the
|
|
* mac address can not be changed.
|
|
*/
|
|
static int rwnx_set_mac_address(struct net_device *dev, void *addr)
|
|
{
|
|
int ret;
|
|
struct sockaddr *sa = addr;
|
|
struct rwnx_vif *rwnx_vif = netdev_priv(dev);
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
|
|
ret = eth_mac_addr(dev, sa);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
|
|
memcpy(rwnx_vif->wdev.address, dev->dev_addr, 6);
|
|
#else
|
|
memcpy(rwnx_vif->address, dev->dev_addr, 6);
|
|
#endif
|
|
|
|
//rwnx_platform_set_mac_addr();
|
|
return ret;
|
|
}
|
|
|
|
const struct net_device_ops rwnx_netdev_ops = {
|
|
.ndo_open = rwnx_open,
|
|
.ndo_stop = rwnx_close,
|
|
.ndo_do_ioctl = rwnx_do_ioctl,
|
|
.ndo_start_xmit = rwnx_start_xmit,
|
|
//.ndo_get_stats = rwnx_get_stats,
|
|
//.ndo_select_queue = rwnx_select_queue,
|
|
.ndo_set_mac_address = rwnx_set_mac_address,
|
|
//.ndo_set_features = rwnx_set_features,
|
|
//.ndo_set_rx_mode = rwnx_set_multicast_list,
|
|
};
|
|
|
|
void rwnx_netdev_setup(struct net_device *dev)
|
|
{
|
|
ether_setup(dev);
|
|
dev->priv_flags &= ~IFF_TX_SKB_SHARING;
|
|
dev->netdev_ops = &rwnx_netdev_ops;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
|
|
dev->destructor = free_netdev;
|
|
#else
|
|
dev->needs_free_netdev = true;
|
|
#endif
|
|
dev->watchdog_timeo = RWNX_TX_LIFETIME_MS;
|
|
|
|
dev->needed_headroom = sizeof(struct rwnx_txhdr) + RWNX_SWTXHDR_ALIGN_SZ;
|
|
#ifdef CONFIG_RWNX_AMSDUS_TX
|
|
dev->needed_headroom = max(dev->needed_headroom,
|
|
(unsigned short)(sizeof(struct rwnx_amsdu_txhdr)
|
|
+ sizeof(struct ethhdr) + 4
|
|
+ sizeof(rfc1042_header) + 2));
|
|
#endif /* CONFIG_RWNX_AMSDUS_TX */
|
|
|
|
dev->hw_features = 0;
|
|
}
|
|
|
|
|
|
#elif defined(CONFIG_RAWDATA_MODE)
|
|
|
|
#define NLAIC_GENL_NAME "nlaic"
|
|
#define NLAIC_GENL_VERSION 0x1
|
|
|
|
/*
|
|
* Commands sent from userspace
|
|
*/
|
|
|
|
enum {
|
|
NLAIC_CMD_UNSPEC = 0, /* Reserved */
|
|
NLAIC_CMD_ECHO, /* user->kernel request/get-response */
|
|
NLAIC_CMD_REPLY, /* kernel->user event */
|
|
NLAIC_CMD_REGISTER_USERID, /* user register portid */
|
|
NLAIC_CMD_RAWDATA, /* tx/rx raw binary data */
|
|
|
|
/* add new commands above here */
|
|
|
|
/* used to define NUM_NLAIC_CMD & NLAIC_CMD_MAX below */
|
|
__NLAIC_CMD_AFTER_LAST,
|
|
NUM_NLAIC_CMD = __NLAIC_CMD_AFTER_LAST,
|
|
NLAIC_CMD_MAX = __NLAIC_CMD_AFTER_LAST - 1
|
|
};
|
|
|
|
enum {
|
|
NLAIC_ATTR_UNSPEC = 0,
|
|
NLAIC_ATTR_MESG, /* message string */
|
|
NLAIC_ATTR_WORDVAL, /* word value */
|
|
NLAIC_ATTR_USERID, /* userid */
|
|
NLAIC_ATTR_RAWDATA, /* raw binary data */
|
|
|
|
/* add new attributes above here */
|
|
|
|
/* used to define NUM_NLAIC_ATTR & NLAIC_ATTR_MAX below */
|
|
__NLAIC_ATTR_AFTER_LAST,
|
|
NUM_NLAIC_ATTR = __NLAIC_ATTR_AFTER_LAST,
|
|
NLAIC_ATTR_MAX = __NLAIC_ATTR_AFTER_LAST - 1
|
|
};
|
|
|
|
struct nlaic_userid_s {
|
|
struct net *net;
|
|
u32 portid;
|
|
};
|
|
|
|
static struct nlaic_userid_s nlaic_userid = {
|
|
.net = NULL,
|
|
.portid = 0
|
|
};
|
|
|
|
#define NLAIC_MESG_LEN_MAX 1024
|
|
#define NLAIC_RAWDATA_LEN_MAX 1600
|
|
|
|
// DEBUG
|
|
#define DBG_RX_RAWDATA_EN 1
|
|
|
|
#if (DBG_RX_RAWDATA_EN)
|
|
int dbg_rx_cnt = 0;
|
|
int dbg_eagain_cnt = 0;
|
|
u64 dbg_start_ms = 0;
|
|
#endif
|
|
|
|
int nlaic_handle_echo(struct sk_buff *skb, struct genl_info *info);
|
|
int nlaic_register_userid(struct sk_buff *skb, struct genl_info *info);
|
|
int nlaic_tx_rawdata(struct sk_buff *skb, struct genl_info *info);
|
|
|
|
static const struct nla_policy nlaic_policy[NUM_NLAIC_ATTR] = {
|
|
[NLAIC_ATTR_MESG] = { .type = NLA_STRING, .len = NLAIC_MESG_LEN_MAX },
|
|
[NLAIC_ATTR_WORDVAL] = { .type = NLA_S32, .len = sizeof(s32) },
|
|
[NLAIC_ATTR_USERID] = { .type = NLA_S32, .len = sizeof(s32) },
|
|
[NLAIC_ATTR_RAWDATA] = { .type = NLA_BINARY, .len = NLAIC_RAWDATA_LEN_MAX },
|
|
};
|
|
|
|
static const struct genl_ops nlaic_ops[] = {
|
|
{
|
|
.cmd = NLAIC_CMD_ECHO,
|
|
.doit = nlaic_handle_echo,
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,2,0))
|
|
.policy = nlaic_policy,
|
|
#endif
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = NLAIC_CMD_REGISTER_USERID,
|
|
.doit = nlaic_register_userid,
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,2,0))
|
|
.policy = nlaic_policy,
|
|
#endif
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
{
|
|
.cmd = NLAIC_CMD_RAWDATA,
|
|
.doit = nlaic_tx_rawdata,
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,2,0))
|
|
.policy = nlaic_policy,
|
|
#endif
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
};
|
|
|
|
static struct genl_family nlaic_family = {
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0))
|
|
.id = GENL_ID_GENERATE,
|
|
#else
|
|
.ops = nlaic_ops,
|
|
.n_ops = sizeof(nlaic_ops) / sizeof(struct genl_ops),
|
|
#endif
|
|
.name = NLAIC_GENL_NAME,
|
|
.version = NLAIC_GENL_VERSION,
|
|
.maxattr = NLAIC_ATTR_MAX,
|
|
};
|
|
|
|
static int nlaic_alloc_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp, size_t size)
|
|
{
|
|
struct sk_buff *skb;
|
|
void *reply;
|
|
|
|
/*
|
|
* If new attributes are added, please revisit this allocation
|
|
*/
|
|
skb = genlmsg_new(size, GFP_KERNEL);
|
|
if (!skb)
|
|
return -ENOMEM;
|
|
|
|
if (!info)
|
|
return -EINVAL;
|
|
|
|
reply = genlmsg_put_reply(skb, info, &nlaic_family, 0, cmd);
|
|
if (reply == NULL) {
|
|
nlmsg_free(skb);
|
|
return -EINVAL;
|
|
}
|
|
|
|
*skbp = skb;
|
|
return 0;
|
|
}
|
|
|
|
static int nlaic_fill_reply(struct sk_buff *skb, int aggr, void *data, int len)
|
|
{
|
|
/* add a netlink attribute to a socket buffer */
|
|
return nla_put(skb, aggr, len, data);
|
|
}
|
|
|
|
static int nlaic_send_reply(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
|
|
void *reply = genlmsg_data(genlhdr);
|
|
|
|
genlmsg_end(skb, reply);
|
|
|
|
return genlmsg_reply(skb, info);
|
|
}
|
|
|
|
static int cmd_attr_echo_message(struct genl_info *info)
|
|
{
|
|
struct nlattr *na;
|
|
char *msg;
|
|
struct sk_buff *rep_skb;
|
|
size_t size;
|
|
int ret;
|
|
|
|
na = info->attrs[NLAIC_ATTR_MESG];
|
|
if (!na)
|
|
return -EINVAL;
|
|
|
|
msg = (char *)nla_data(na);
|
|
pr_info("demo generic netlink receive echo mesg %s\n", msg);
|
|
|
|
size = nla_total_size(strlen(msg)+1);
|
|
|
|
ret = nlaic_alloc_reply(info, NLAIC_CMD_REPLY, &rep_skb, size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = nlaic_fill_reply(rep_skb, NLAIC_ATTR_MESG, msg, size);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
return nlaic_send_reply(rep_skb, info);
|
|
|
|
err:
|
|
nlmsg_free(rep_skb);
|
|
return ret;
|
|
}
|
|
|
|
static int cmd_attr_echo_data(struct genl_info *info)
|
|
{
|
|
struct nlattr *na;
|
|
s32 data;
|
|
struct sk_buff *rep_skb;
|
|
size_t size;
|
|
int ret;
|
|
|
|
na = info->attrs[NLAIC_ATTR_WORDVAL];
|
|
if (!na)
|
|
return -EINVAL;
|
|
|
|
data = nla_get_s32(info->attrs[NLAIC_ATTR_WORDVAL]);
|
|
pr_info("demo generic netlink receive echo data %d\n", data);
|
|
|
|
size = nla_total_size(sizeof(s32));
|
|
|
|
ret = nlaic_alloc_reply(info, NLAIC_CMD_REPLY, &rep_skb, size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = nla_put_s32(rep_skb, NLAIC_ATTR_WORDVAL, data);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
return nlaic_send_reply(rep_skb, info);
|
|
|
|
err:
|
|
nlmsg_free(rep_skb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
u64 get_systime_ms(void)
|
|
{
|
|
u64 cur_ms;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
|
|
struct timespec ts;
|
|
get_monotonic_boottime(&ts);
|
|
cur_ms = (u64)ts.tv_sec * 1000 + div_u64(ts.tv_nsec, 1000000);
|
|
#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
|
|
struct timespec ts;
|
|
ts = ktime_to_timespec(ktime_get_boottime());
|
|
cur_ms = (u64)ts.tv_sec * 1000 + div_u64(ts.tv_nsec, 1000000);
|
|
#else
|
|
struct timespec64 ts;
|
|
ts = ktime_to_timespec64(ktime_get_boottime());
|
|
cur_ms = (u64)ts.tv_sec * 1000 + div_u64(ts.tv_nsec, 1000000);
|
|
#endif
|
|
return cur_ms;
|
|
}
|
|
|
|
int nlaic_rx_rawdata(u8 *data, int len)
|
|
{
|
|
int ret = 0;
|
|
if (nlaic_userid.net) {
|
|
struct sk_buff *skb;
|
|
size_t size;
|
|
void *hdr;
|
|
size = nla_total_size(len);
|
|
skb = genlmsg_new(size, GFP_KERNEL);
|
|
if (!skb) {
|
|
printk("genlmsg_new failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
hdr = genlmsg_put(skb, nlaic_userid.portid, 0, &nlaic_family, 0, NLAIC_CMD_RAWDATA);
|
|
if (!hdr) {
|
|
printk("genlmsg_put failed\n");
|
|
nlmsg_free(skb);
|
|
return -ENOMEM;
|
|
}
|
|
nla_put(skb, NLAIC_ATTR_RAWDATA, len, data);
|
|
genlmsg_end(skb, hdr);
|
|
#if 1
|
|
ret = genlmsg_unicast(nlaic_userid.net, skb, nlaic_userid.portid);
|
|
#if (DBG_RX_RAWDATA_EN)
|
|
if (ret == -EAGAIN) {
|
|
dbg_eagain_cnt++;
|
|
ret = 0;
|
|
}
|
|
#endif
|
|
#else
|
|
do {
|
|
ret = genlmsg_unicast(nlaic_userid.net, skb, nlaic_userid.portid);
|
|
if (!ret) {
|
|
break;
|
|
} else if (ret == -EAGAIN) {
|
|
#if (DBG_RX_RAWDATA_EN)
|
|
if (retry_cnt == 0) {
|
|
dbg_eagain_cnt++;
|
|
}
|
|
#endif
|
|
retry_cnt++;
|
|
if (retry_cnt > 4) {
|
|
printk("retry %d times, giveup\n", retry_cnt);
|
|
ret = 0;
|
|
break;
|
|
}
|
|
} else {
|
|
printk("genlmsg_unicast failed, ret=%d\n", ret);
|
|
break;
|
|
}
|
|
} while (1);
|
|
#endif
|
|
#if (DBG_RX_RAWDATA_EN)
|
|
dbg_rx_cnt++;
|
|
if ((char)data[1] == 'S') {
|
|
dbg_start_ms = get_systime_ms();
|
|
printk("[%lld] start", dbg_start_ms);
|
|
} else if ((char)data[1] == 'E') {
|
|
u64 dbg_end_ms = get_systime_ms();
|
|
printk("[%lld] cnt=%d,eag=%d,cost=%lld\n", dbg_end_ms, dbg_rx_cnt, dbg_eagain_cnt, (dbg_end_ms - dbg_start_ms));
|
|
}
|
|
#endif
|
|
} else {
|
|
ret = -EPERM;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int nlaic_handle_echo(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
if (info->attrs[NLAIC_ATTR_MESG])
|
|
return cmd_attr_echo_message(info);
|
|
else if (info->attrs[NLAIC_ATTR_WORDVAL])
|
|
return cmd_attr_echo_data(info);
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
|
|
int nlaic_register_userid(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
if (!info->attrs[NLAIC_ATTR_USERID]) {
|
|
return -EINVAL;
|
|
}
|
|
//if (!nlaic_userid.net)
|
|
{
|
|
nlaic_userid.net = genl_info_net(info);
|
|
nlaic_userid.portid = info->snd_portid;
|
|
//printk("nlaic_register_userid, portid=%d\n", info->snd_portid);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int nlaic_tx_rawdata(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
int ret, len;
|
|
u8 *buf;
|
|
struct sk_buff *new_skb;
|
|
struct nlattr *na = info->attrs[NLAIC_ATTR_RAWDATA];
|
|
int headroom = 4;
|
|
if (!na) {
|
|
return -EINVAL;
|
|
}
|
|
buf = nla_data(na);
|
|
len = nla_len(na);
|
|
new_skb = dev_alloc_skb(len + headroom);
|
|
if (!new_skb) {
|
|
return -ENOMEM;
|
|
}
|
|
skb_pull(new_skb, headroom);
|
|
/*printk("nlaic_tx_rawdata, len=%d\n", len);
|
|
int idx;
|
|
for (idx = 0; idx < len; idx+=4) {
|
|
printk(" %02x %02x %02x %02x\n", buf[idx+0], buf[idx+1], buf[idx+2], buf[idx+3]);
|
|
}*/
|
|
memcpy(skb_put(new_skb, len), buf, len);
|
|
ret = rwnx_tx_data(new_skb);
|
|
if (ret) {
|
|
dev_kfree_skb(new_skb);
|
|
pr_err("tx data fail, %d\n", ret);
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int rwnx_nlaic_init(struct rwnx_plat *rwnx_plat, void **platform_data)
|
|
{
|
|
int ret;
|
|
pr_info("demo generic netlink module %d init...\n", NLAIC_GENL_VERSION);
|
|
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0))
|
|
ret = genl_register_ops(&nlaic_family, nlaic_ops);
|
|
#elif (LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0))
|
|
ret = genl_register_family_with_ops(&nlaic_family, nlaic_ops);
|
|
#else
|
|
ret = genl_register_family(&nlaic_family);
|
|
#endif
|
|
if (ret != 0) {
|
|
pr_err("failed to init demo generic netlink example module\n");
|
|
return ret;
|
|
}
|
|
|
|
pr_info("demo generic netlink module init success\n");
|
|
return 0;
|
|
}
|
|
|
|
void rwnx_nlaic_deinit(void)
|
|
{
|
|
int ret;
|
|
pr_info("demo generic netlink deinit.\n");
|
|
|
|
ret = genl_unregister_family(&nlaic_family);
|
|
if(ret != 0) {
|
|
pr_err("faled to unregister family:%i\n", ret);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*********************************************************************
|
|
* Init/Exit functions
|
|
*********************************************************************/
|
|
|
|
static void aicsmac_driver_register(void)
|
|
{
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
aicwf_sdio_register();
|
|
#endif
|
|
#ifdef AICWF_USB_SUPPORT
|
|
aicwf_usb_register();
|
|
#endif
|
|
#ifdef AICWF_PCIE_SUPPORT
|
|
aicwf_pcie_register();
|
|
#endif
|
|
}
|
|
|
|
//static DECLARE_WORK(aicsmac_driver_work, aicsmac_driver_register);
|
|
|
|
struct completion hostif_register_done;
|
|
|
|
#define REGISTRATION_TIMEOUT 6000
|
|
|
|
void aicwf_hostif_ready(void)
|
|
{
|
|
complete(&hostif_register_done);
|
|
}
|
|
|
|
#define NB_DEVICE 2
|
|
#define TERM_DEV_ID 0
|
|
#define TRACE_DEV_ID 1
|
|
|
|
extern const struct file_operations rwnx_trace_ops;
|
|
extern const struct file_operations rwnx_term_ops;
|
|
|
|
static int __init rwnx_mod_init(void)
|
|
{
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
rwnx_print_version();
|
|
printk("RELEASE DATE:%s \r\n", RELEASE_DATE);
|
|
|
|
init_completion(&hostif_register_done);
|
|
|
|
aicsmac_driver_register();
|
|
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
if ((wait_for_completion_timeout(&hostif_register_done, msecs_to_jiffies(REGISTRATION_TIMEOUT)) == 0)) {
|
|
printk("register_driver timeout or error\n");
|
|
aicwf_sdio_exit();
|
|
return -ENODEV;
|
|
}
|
|
#endif /* AICWF_SDIO_SUPPORT */
|
|
|
|
#ifdef AICWF_USB_SUPPORT
|
|
if ((wait_for_completion_timeout(&hostif_register_done, msecs_to_jiffies(REGISTRATION_TIMEOUT)) == 0)) {
|
|
printk("register_driver timeout or error\n");
|
|
aicwf_usb_exit();
|
|
return -ENODEV;
|
|
}
|
|
#endif /*AICWF_USB_SUPPORT */
|
|
|
|
|
|
#ifdef AICWF_PCIE_SUPPORT
|
|
return rwnx_platform_register_drv();
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static void __exit rwnx_mod_exit(void)
|
|
{
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
|
|
#ifdef AICWF_PCIE_SUPPORT
|
|
rwnx_platform_unregister_drv();
|
|
#endif
|
|
|
|
#ifdef AICWF_SDIO_SUPPORT
|
|
aicwf_sdio_exit();
|
|
#endif
|
|
|
|
#ifdef AICWF_USB_SUPPORT
|
|
aicwf_usb_exit();
|
|
#endif
|
|
}
|
|
|
|
module_init(rwnx_mod_init);
|
|
module_exit(rwnx_mod_exit);
|
|
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
|
|
|
|
MODULE_FIRMWARE(RWNX_CONFIG_FW_NAME);
|
|
|
|
MODULE_DESCRIPTION(RW_DRV_DESCRIPTION);
|
|
MODULE_VERSION(RWNX_VERS_MOD);
|
|
MODULE_AUTHOR(RW_DRV_COPYRIGHT " " RW_DRV_AUTHOR);
|
|
MODULE_LICENSE("GPL");
|
|
|