0
0
Fork 0
hgicf/hgic_fmac/core.c

994 lines
29 KiB
C
Raw Normal View History

2023-05-16 09:13:19 +00:00
#ifdef __RTOS__
#include <linux/types.h>
#include <linux/unaligned.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/completion.h>
#include <linux/rcu.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#else
#include <linux/version.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/firmware.h>
#include <linux/workqueue.h>
#include <linux/wireless.h>
#include <linux/etherdevice.h>
#include <linux/version.h>
#include <linux/rtnetlink.h>
#endif
#include "hgicf.h"
#include "ctrl.h"
#include "cfg.h"
#include "event.h"
static int txq_size = 1024;
static char *conf_file = "/etc/hgicf.conf";
static char *fw_file = "hgicf.bin";
static int if_test = 0;
static int soft_fc = 1;
static int no_bootdl = 0;
static int qc_mode = 0;
static char *ifname = "hg%d";
static hgic_init_cb init_cb = NULL;
#ifdef __RTOS__
static hgic_event_cb event_cb = NULL;
#endif
#define TXWND_INIT_VAL (2)
#define MATCH_NDEV(fwidx,hg) ((fwidx) == HGIC_WDEV_ID_STA ? (hg->sta ? hg->sta->ndev : NULL) : \
(hg->ap ? hg->ap->ndev : NULL))
#ifdef __RTOS__
static int iface_cnt = 1;
void dev_queue_xmit(struct sk_buff *skb)
{
struct net_device *ndev = skb->dev;
if (ndev) {
ndev->netdev_ops->ndo_start_xmit(skb, ndev);
} else {
kfree_skb(skb);
}
}
#endif
u16 hgicf_data_cookie(struct hgicf_wdev *hg)
{
unsigned long flags;
uint16_t cookie = 0;
spin_lock_irqsave(&hg->lock, flags);
cookie = hg->data_cookie++;
spin_unlock_irqrestore(&hg->lock, flags);
return cookie;
}
void hgicf_load_config(struct hgicf_wdev *hg)
{
#ifndef __RTOS__
struct file *fp = NULL;
struct iwreq wrqin;
struct net_device *ndev = NULL;
ssize_t ret = 0;
unsigned long long __maybe_unused offset = 0 ;
char *conf = kzalloc(2048, GFP_KERNEL);
char *ptr, *str;
if (hg->sta) {ndev = hg->sta->ndev; }
else if (hg->ap) {ndev = hg->ap->ndev; }
else {
hgic_err("No netdev!! \r\n");
goto __out;
}
fp = filp_open(conf_file, O_RDONLY, 0);
if (!IS_ERR(fp) && conf) {
ret = _KERNEL_READ(fp, conf, 2048, &offset);
str = conf;
hgic_fwctrl_close_dev(&(hg->ctrl));
while (str) {
ptr = strstr(str, "\r\n");
if (ptr) {
*ptr = 0;
ptr += 2;
} else {
ptr = strstr(str, "\n");
if (ptr) { *ptr++ = 0;}
}
wrqin.u.data.length = strlen(str);
if (wrqin.u.data.length > 0) {
hgic_dbg("param: [%s]\r\n", str);
wrqin.u.data.pointer = str;
ret = hgicf_ioctl_set_proc(ndev, &wrqin);
if (ret < 0) {
hgic_err("invalid param:[%s]\r\n", str);
}
}
str = ptr;
}
hgic_fwctrl_open_dev(&(hg->ctrl));
}
__out:
if (!IS_ERR(fp)) { filp_close(fp, NULL); }
if (conf) { kfree(conf); }
#endif
}
static int hgicf_netif_open(struct net_device *dev)
{
struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev);
if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) {
return 0;
}
vif->hg->radio_off = 0;
return hgic_fwctrl_open_dev(&(vif->hg->ctrl));
}
static int hgicf_netif_stop(struct net_device *dev)
{
struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev);
if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) {
return 0;
}
vif->hg->radio_off = 1;
return hgic_fwctrl_close_dev(&(vif->hg->ctrl));
}
static void hgicf_netif_uninit(struct net_device *dev)
{
struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev);
if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) {
return;
}
}
static netdev_tx_t hgicf_netif_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev);
struct hgic_frm_hdr2 *hdr = NULL;
u8 pad = ((ulong)skb->data & 0x3) ? (4 - ((ulong)skb->data & 0x3)) : 0;
if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags) ||
(vif->hg->tx_dataq.qlen > txq_size) ||
test_bit(HGIC_BUS_FLAGS_SLEEP, &vif->hg->bus->flags)) {
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
#ifndef __RTOS__
if (skb_headroom(skb) < sizeof(struct hgic_frm_hdr2)) {
struct sk_buff *nskb = skb_copy_expand(skb, skb->dev->needed_headroom,
skb->dev->needed_tailroom, GFP_KERNEL);
dev_kfree_skb(skb);
if (nskb == NULL) {
return NETDEV_TX_OK;
}
skb = nskb;
pad = ((ulong)skb->data & 0x3) ? (4 - ((ulong)skb->data & 0x3)) : 0;
}
#endif
hdr = (struct hgic_frm_hdr2 *)skb_push(skb, sizeof(struct hgic_frm_hdr2) + pad);
hdr->hdr.ifidx = vif->fwifidx;
hdr->hdr.length = skb->len;
hdr->hdr.magic = HGIC_HDR_TX_MAGIC;
hdr->hdr.type = HGIC_HDR_TYPE_FRM2;
hdr->hdr.flags = pad;
hdr->hdr.cookie = hgicf_data_cookie(vif->hg);
skb_queue_tail(&vif->hg->tx_dataq, skb);
queue_work(vif->hg->tx_wq, &vif->hg->tx_work);
return NETDEV_TX_OK;
}
static int hgicf_netif_change_mac(struct net_device *dev, void *addr)
{
int ret = 0;
#ifdef __RTOS__
ret = hgicf_ioctl(dev, HGIC_CMD_SET_MAC, addr, 0);
#else
struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev);
struct sockaddr *sa = (struct sockaddr *)addr;
if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) {
return 0;
}
ret = hgic_fwctrl_set_mac(&(vif->hg->ctrl), sa->sa_data);
if (!ret) {
ret = eth_mac_addr(dev, addr);
}
#endif
return ret;
}
static void hgicf_netif_set_multicast_list(struct net_device *dev)
{
struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev);
if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) {
return;
}
}
#ifdef __RTOS__
static int hgicf_netif_ioctl(struct net_device *dev, u32 cmd, u32 param1, u32 param2)
{
return hgicf_ioctl(dev, cmd, param1, param2);
}
int hgicf_cmd(char *ifname, unsigned int cmd, unsigned int param1, unsigned int param2)
{
struct net_device *ndev = net_device_get_by_name(ifname);
if (ndev == NULL) {
return -ENODEV;
}
return hgicf_netif_ioctl(ndev, cmd, param1, param2);
}
#else
static int hgicf_netif_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev);
if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) {
return 0;
}
return hgicf_ioctl(dev, ifr, cmd);
}
#endif
static const struct net_device_ops hgicf_netif_ops = {
.ndo_open = hgicf_netif_open,
.ndo_stop = hgicf_netif_stop,
.ndo_uninit = hgicf_netif_uninit,
.ndo_start_xmit = hgicf_netif_xmit,
.ndo_set_rx_mode = hgicf_netif_set_multicast_list,
.ndo_set_mac_address = hgicf_netif_change_mac,
.ndo_do_ioctl = hgicf_netif_ioctl,
};
static void hgicf_netif_setup(struct net_device *dev)
{
#ifdef __RTOS__
dev->netdev_ops = &hgicf_netif_ops;
#else
ether_setup(dev);
dev->netdev_ops = &hgicf_netif_ops;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
dev->priv_destructor = free_netdev;
#else
dev->destructor = free_netdev;
#endif
#endif
}
static int hgicf_request_txwnd(struct hgic_bus *bus)
{
struct hgic_hdr *hdr = NULL;
struct sk_buff *skb = dev_alloc_skb(sizeof(struct hgic_hdr) + 2);
if (skb == NULL) { return -ENOMEM; }
hdr = (struct hgic_hdr *)skb->data;
hdr->magic = HGIC_HDR_TX_MAGIC;
hdr->type = HGIC_HDR_TYPE_SOFTFC;
hdr->length = sizeof(struct hgic_hdr) + 2;
hdr->ifidx = 0;
hdr->flags = 0;
hdr->cookie = 0;
skb_put(skb, sizeof(struct hgic_hdr) + 2);
return bus->tx_packet(bus, skb);
}
static int hgicf_check_txwnd(struct hgicf_wdev *hg)
{
int err = 0;
if (!hg->soft_fc || test_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags)) {
return 0;
}
while (hg->soft_fc && atomic_read(&hg->txwnd) < TXWND_INIT_VAL &&
test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags) &&
!test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags) &&
!test_bit(HGICF_DEV_FLAGS_SUSPEND, &hg->flags) &&
!test_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags)) {
err = hgicf_request_txwnd(hg->bus);
if (!err) {
wait_for_completion_timeout(&hg->txwnd_cp, msecs_to_jiffies(100));
}
if(atomic_read(&hg->txwnd) < TXWND_INIT_VAL)
msleep(10);
}
if (!hg->soft_fc || test_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags)) {
return 0;
}
if (atomic_read(&hg->txwnd) < TXWND_INIT_VAL) {
return -1;
} else {
atomic_dec(&hg->txwnd);
}
return 0;
}
static void hgicf_test_work(struct work_struct *work)
{
int ret = 0;
u32 diff_jiff = 0;
struct hgicf_wdev *hg = NULL;
struct sk_buff *skb = NULL;
struct hgic_frm_hdr *frmhdr = NULL;
printk("start if test ...\r\n");
hg = container_of(work, struct hgicf_wdev, test_work);
hg->test_jiff = jiffies;
//set_bit(HGIC_BUS_FLAGS_DISABLE_REINIT, &hg->bus->flags);
while (test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags)) {
if (time_after(jiffies, hg->test_jiff + msecs_to_jiffies(5000))) {
diff_jiff = jiffies_to_msecs(jiffies - hg->test_jiff);
diff_jiff /= 1000;
if (diff_jiff == 0) { diff_jiff = 0xffff; }
printk("HGIC IF TEST: tx:%d KB/s, rx:%d KB/s (%d %d %d)\r\n",
(hg->test_tx_len / 1024) / diff_jiff,
(hg->test_rx_len / 1024) / diff_jiff,
hg->test_tx_len, hg->test_rx_len, diff_jiff);
hg->test_rx_len = 0;
hg->test_tx_len = 0;
hg->test_jiff = jiffies;
}
skb = dev_alloc_skb(1500 + hg->bus->drv_tx_headroom);
if (skb) {
skb_reserve(skb, hg->bus->drv_tx_headroom);
frmhdr = (struct hgic_frm_hdr *)skb->data;
frmhdr->hdr.magic = HGIC_HDR_TX_MAGIC;
frmhdr->hdr.type = (hg->if_test == 1 ? HGIC_HDR_TYPE_TEST : HGIC_HDR_TYPE_TEST2);
frmhdr->hdr.length = 1500;
frmhdr->hdr.ifidx = 0;
frmhdr->hdr.cookie = hgicf_data_cookie(hg);
if(hg->if_test == 3) memset(skb->data+8, 0xAA, 1500-8);
skb_put(skb, 1500);
while (test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags) && hgicf_check_txwnd(hg)) {
msleep(10);
}
ret = hg->bus->tx_packet(hg->bus, skb);
if (ret) {
msleep(10);
}
} else {
msleep(10);
}
}
printk("if test stop!\r\n");
}
static void hgicf_tx_work(struct work_struct *work)
{
struct sk_buff *skb = NULL;
struct hgicf_wdev *hg = container_of(work, struct hgicf_wdev, tx_work);
struct hgic_hdr *hdr = NULL;
//hgic_dbg("Enter\n");
_CTRLQ_TX:
while (!skb_queue_empty(&hg->ctrl.txq)) {
if (test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags) ||
!test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags)) {
hgic_clear_queue(&hg->ctrl.txq);
goto _CTRLQ_TX;
}
if (hgicf_check_txwnd(hg)) {
msleep(10);
continue;
}
skb = skb_dequeue(&hg->ctrl.txq);
if(skb){
hdr = (struct hgic_hdr *)skb->data;
switch(hdr->type){
case HGIC_HDR_TYPE_BOOTDL:
break;
case HGIC_HDR_TYPE_BOOTDL_DATA:
skb_pull(skb, sizeof(struct hgic_bootdl_cmd_hdr));
break;
default:
if(test_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags)){
kfree_skb(skb);
skb = NULL;
}
break;
}
}
if(skb){
hg->bus->tx_packet(hg->bus, skb);
}
}
while (!skb_queue_empty(&hg->tx_dataq) &&
test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags) &&
!test_bit(HGICF_DEV_FLAGS_SUSPEND, &hg->flags)) {
if (test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags) ||
test_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags)) {
hgic_clear_queue(&hg->tx_dataq);
goto _CTRLQ_TX;
}
if (hgicf_check_txwnd(hg)) {
msleep(10);
goto _CTRLQ_TX;
}
skb = skb_dequeue(&hg->tx_dataq);
hg->bus->tx_packet(hg->bus, skb);
if (!skb_queue_empty(&hg->ctrl.txq)) {
goto _CTRLQ_TX;
}
}
//hgic_dbg("Leave\n");
}
static struct hgicf_vif *hgicf_create_iface(struct hgicf_wdev *hg)
{
int ret = 0;
struct net_device *ndev = NULL;
struct hgicf_vif *vif = NULL;
hgic_dbg("Enter\n");
ndev = ALLOC_NETDEV_MQS(sizeof(struct hgicf_vif), ifname, hgicf_netif_setup, 1, 1);
if (!ndev) {
hgic_err("%s: alloc_netdev_mqs failed\n", __func__);
return NULL;
}
vif = (struct hgicf_vif *)netdev_priv(ndev);
vif->ndev = ndev;
vif->hg = hg;
vif->state = 0;
ndev->needed_headroom += (hg->bus->drv_tx_headroom + sizeof(struct hgic_frm_hdr2));
memcpy(ndev->dev_addr, hg->fwinfo.mac, ETH_ALEN);
ret = register_netdev(ndev);
if (ret) {
free_netdev(ndev);
return NULL;
}
#ifndef __RTOS__
rtnl_lock();
DEV_OPEN(ndev);
rtnl_unlock();
#endif
return vif;
}
static int hgicf_rx_data(void *hgobj, struct sk_buff *skb, int len)
{
int i = 0;
struct hgic_frm_hdr2 *hdr = NULL;
struct hgicf_wdev *hg = hgobj;
hgic_skip_padding(skb);
hdr = (struct hgic_frm_hdr2 *) skb->data;
skb->dev = MATCH_NDEV(hdr->hdr.ifidx, hg);
if (hdr->hdr.magic != HGIC_HDR_RX_MAGIC) {
hgic_err("invalid magic unmber:%x\r\n", hdr->hdr.magic);
hgic_print_hex(skb->data, 16);
dev_kfree_skb_any(skb);
return -1;
}
if (hdr->hdr.type != HGIC_HDR_TYPE_BOOTDL) {
if (len < hdr->hdr.length) {
hgic_err("invalid data length: %x/%x,cookie:%d\r\n", len, hdr->hdr.length, hdr->hdr.cookie);
dev_kfree_skb_any(skb);
return -1;
}
}
skb_put(skb, len > hdr->hdr.length ? hdr->hdr.length : len);
switch (hdr->hdr.type) {
case HGIC_HDR_TYPE_FRM2:
hg->last_rx = jiffies;
if (skb->dev == NULL || hg->if_test) {
dev_kfree_skb_any(skb);
break;
}
skb_pull(skb, sizeof(struct hgic_frm_hdr2));
skb->protocol = eth_type_trans(skb, skb->dev);
if (in_interrupt()) {
netif_rx(skb);
} else {
netif_rx_ni(skb);
}
break;
case HGIC_HDR_TYPE_CMD:
case HGIC_HDR_TYPE_CMD2:
case HGIC_HDR_TYPE_EVENT:
case HGIC_HDR_TYPE_EVENT2:
case HGIC_HDR_TYPE_OTA:
case HGIC_HDR_TYPE_BOOTDL:
if (hg->if_test) {
dev_kfree_skb_any(skb);
break;
}
hgic_fwctrl_rx(&hg->ctrl, skb);
break;
case HGIC_HDR_TYPE_TEST2:
hg->last_rx = jiffies;
hg->test_rx_len += len;
if (hg->if_test == 3) {
for (i = 8; i < 1500; i++) {
if (skb->data[i] != 0xAA) {
printk("data verify fail\r\n");
break;
}
}
}
dev_kfree_skb(skb);
break;
case HGIC_HDR_TYPE_SOFTFC:
hg->last_rx = jiffies;
atomic_set(&hg->txwnd, hdr->hdr.cookie);
complete(&hg->txwnd_cp);
dev_kfree_skb_any(skb);
break;
default:
hgic_err("invalid data:%d\r\n", hdr->hdr.type);
dev_kfree_skb_any(skb);
break;
}
return 0;
}
static void hgicf_tx_complete(void *hgobj, struct sk_buff *skb, int success)
{
struct hgicf_wdev *hg = hgobj;
if (hg->if_test) {
hg->test_tx_len += skb->len;
}
dev_kfree_skb_any(skb);
if (!success) {
hgic_err("tx failed\r\n");
}
}
static int hgicf_download_fw(struct hgicf_wdev *hg)
{
int err = -1;
int retry = 10;
int status = -1;
hgic_dbg("Enter\n");
if (no_bootdl || qc_mode) { status = STATE_FW; }
set_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags);
while (status != STATE_FW && retry-- > 0 && err) {
status = hgic_bootdl_cmd_enter(&hg->bootdl);
if (status == STATE_BOOT) {
err = hgic_bootdl_download(&hg->bootdl, fw_file);
}
}
clear_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags);
if (status == STATE_BOOT && !err) {
set_bit(HGICF_DEV_FLAGS_BOOTDL, &hg->flags);
if (hg->bus->reinit && test_bit(HGIC_BUS_FLAGS_NOPOLL, &hg->bus->flags)) {
msleep(100);
retry = 4;
if (test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags) && !hg->bus->reinit(hg->bus)) {
while (retry-- > 0 && (STATE_FW != status)) {
status = hgic_bootdl_cmd_enter(&hg->bootdl);
}
}
}
}
if(!no_bootdl && hg->bus->reinit){
mod_timer(&hg->alive_tmr, jiffies + msecs_to_jiffies(HGIC_ALIVE_TIMER*4));
}
return (status == STATE_FW);
}
static void hgicf_delay_init(struct work_struct *work)
{
int ret = 0;
u8 retry = 0;
int conn_stat = 0;
struct hgicf_wdev *hg = container_of(work, struct hgicf_wdev, delay_init);
hgic_dbg("Enter\n");
while(!test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags) && test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags)){
if((retry & 0xf) == 0){
ret = hgicf_download_fw(hg);
if(ret) break;
}
msleep(10);
retry++;
}
if (ret) {
clear_bit(HGICF_DEV_FLAGS_SUSPEND, &hg->flags);
hg->last_rx = jiffies;
hg->soft_fc = soft_fc;
conn_stat = hgic_fwctrl_get_conn_state(&hg->ctrl);
#ifndef __RTOS__
hgic_fwctrl_get_dhcpc_result(&hg->ctrl, (u8 *)&hg->dhcpc, sizeof(hg->dhcpc));
#endif
hgic_fwctrl_get_fwinfo(&hg->ctrl, &hg->fwinfo);
printk("hgic fw info:%d.%d.%d.%d, svn version:%d, %pM, conn_state:%d\r\n",
(hg->fwinfo.version >> 24) & 0xff, (hg->fwinfo.version >> 16) & 0xff,
(hg->fwinfo.version >> 8) & 0xff, (hg->fwinfo.version & 0xff),
hg->fwinfo.svn_version, hg->fwinfo.mac, conn_stat);
if (!test_bit(HGICF_DEV_FLAGS_INITED, &hg->flags)) {
hg->sta = hgicf_create_iface(hg);
if (!hg->sta) {
hgic_err("create iface fail, ret:%d\r\n", ret);
return;
}
hg->sta->fwifidx = HGIC_WDEV_ID_STA;
hg->sta->hw_state = conn_stat;
hgicf_create_procfs(hg);
if (init_cb) { init_cb(0); }
if (if_test) {
hg->if_test = if_test;
queue_work(hg->tx_wq, &hg->test_work);
}
set_bit(HGICF_DEV_FLAGS_INITED, &hg->flags);
}
if ((test_bit(HGICF_DEV_FLAGS_BOOTDL, &hg->flags) || conn_stat != HGICF_HW_CONNECTED) && !qc_mode) {
hgicf_load_config(hg);
}
}
hgic_dbg("Leave, ret=%d\n", ret);
}
static void hgicf_alive_work(struct work_struct *work)
{
int retry = 4;
int status = -1;
struct hgicf_wdev *hg = container_of(work, struct hgicf_wdev, alive_work);
//hgic_dbg("SLEEP:%d\r\n", test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags));
if(!test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags)){
if(time_after(jiffies, hg->last_rx + msecs_to_jiffies(HGIC_ALIVE_TIMER))){
//hgic_dbg("detect device status ...\r\n");
while(retry-- > 0 && status != STATE_FW){
status = hgic_bootdl_cmd_enter(&hg->bootdl);
}
if (status != STATE_FW) {
hgic_dbg("need reload firmware ...\r\n");
hg->soft_fc = 0;
set_bit(HGICF_DEV_FLAGS_SUSPEND, &hg->flags);
hgic_clear_queue(&hg->ctrl.txq);
hgic_clear_queue(&hg->tx_dataq);
hg->bus->reinit(hg->bus);
schedule_work(&hg->delay_init);
}
}
}
//hgic_dbg("next detect after %d sec.\r\n", HGIC_ALIVE_TIMER);
mod_timer(&hg->alive_tmr, jiffies + msecs_to_jiffies(HGIC_ALIVE_TIMER));
}
#ifdef __RTOS__
static void hgicf_alive_timer(unsigned long arg)
{
struct hgicf_wdev *hg = (struct hgicf_wdev *) arg;
schedule_work(&hg->alive_work);
}
#else
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)
static void hgicf_alive_timer(struct timer_list *t)
{
struct hgicf_wdev *hg = from_timer(hg, t, alive_tmr);
schedule_work(&hg->alive_work);
}
#else
static void hgicf_alive_timer(unsigned long arg)
{
struct hgicf_wdev *hg = (struct hgicf_wdev *) arg;
schedule_work(&hg->alive_work);
}
#endif
#endif
void hgic_schedule(struct hgic_fwctrl *ctrl)
{
struct hgicf_wdev *hg = container_of(ctrl, struct hgicf_wdev, ctrl);
queue_work(hg->tx_wq, &hg->tx_work);
}
void hgic_rx_fw_event(struct hgic_fwctrl *ctrl, struct sk_buff *skb)
{
struct hgicf_wdev *hg = container_of(ctrl, struct hgicf_wdev, ctrl);
hgicf_rx_fw_event(hg, skb);
}
int hgic_core_remove(void *hgobj)
{
struct hgicf_wdev *hg = hgobj;
if (hg) {
hgic_dbg("Enter\n");
clear_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags);
del_timer_sync(&hg->alive_tmr);
cancel_work_sync(&hg->alive_work);
cancel_work_sync(&hg->delay_init);
hgic_dbg(" trace ...\r\n");
if (hg->ap) {
netif_stop_queue(hg->ap->ndev);
}
if (hg->sta) {
netif_stop_queue(hg->sta->ndev);
}
hgicf_delete_procfs(hg);
hgic_fwctrl_release(&hg->ctrl);
hgic_dbg(" trace ...\r\n");
hgic_ota_release(&hg->ota);
hgic_dbg(" trace ...\r\n");
hgic_bootdl_release(&hg->bootdl, 0);
hgic_dbg(" trace ...\r\n");
//while (!test_bit(HGICF_DEV_FLAGS_STOP, &hg->flags));
cancel_work_sync(&hg->tx_work);
hgic_dbg(" trace ...\r\n");
cancel_work_sync(&hg->test_work);
hgic_dbg(" trace ... %p\r\n", hg->ap);
if (hg->ap) {
unregister_netdev(hg->ap->ndev);
}
hgic_dbg(" trace ... %p\r\n", hg->sta);
if (hg->sta) {
unregister_netdev(hg->sta->ndev);
}
hgic_dbg(" trace ...\r\n");
hgic_clear_queue(&hg->tx_dataq);
hgic_dbg(" trace ...\r\n");
#ifdef __RTOS__
skb_queue_head_deinit(&hg->tx_dataq);
#else
hgic_clear_queue(&hg->custmgmt_q);
hgic_clear_queue(&hg->dbginfo_q);
hgic_clear_queue(&hg->cust_driverdata_q);
#endif
hgic_dbg(" trace ...\r\n");
if (hg->tx_wq) {
flush_workqueue(hg->tx_wq);
destroy_workqueue(hg->tx_wq);
}
hgic_dbg(" trace ...\r\n");
#ifdef __RTOS__
deinit_completion(&hg->txwnd_cp);
spin_lock_deinit(&hg->lock);
#endif
if(hg->paired_stas)
kfree(hg->paired_stas);
kfree(hg);
hgic_dbg("Leave\n");
}
return 0;
}
EXPORT_SYMBOL(hgic_core_remove);
int hgic_core_probe(void *dev, struct hgic_bus *bus)
{
struct hgicf_wdev *hg = NULL;
hgic_dbg("Enter,qc_mode=%d, no_bootdl=%d\n", qc_mode, no_bootdl);
hg = kzalloc(sizeof(struct hgicf_wdev), GFP_KERNEL);
if (hg) {
memset(hg, 0, sizeof(struct hgicf_wdev));
hg->ctrl.qc_mode = qc_mode;
hg->bus = bus;
hg->dev = dev;
hg->dev_id = bus->dev_id;
hgic_fwctrl_init(&hg->ctrl, dev);
hgic_ota_init(&hg->ota, &hg->ctrl, &hg->fwinfo);
spin_lock_init(&hg->lock);
INIT_WORK(&hg->tx_work, hgicf_tx_work);
INIT_WORK(&hg->delay_init, hgicf_delay_init);
INIT_WORK(&hg->alive_work, hgicf_alive_work);
INIT_WORK(&hg->test_work, hgicf_test_work);
init_timer(&hg->alive_tmr);
setup_timer(&hg->alive_tmr, hgicf_alive_timer, (unsigned long)hg);
skb_queue_head_init(&hg->tx_dataq);
init_completion(&hg->txwnd_cp);
hgic_bootdl_init(&hg->bootdl, hg->bus, &hg->ctrl);
#ifndef __RTOS__
skb_queue_head_init(&hg->custmgmt_q);
skb_queue_head_init(&hg->dbginfo_q);
skb_queue_head_init(&hg->cust_driverdata_q);
sema_init(&hg->evtq.sema, 0);
#endif
atomic_set(&hg->txwnd, TXWND_INIT_VAL);
bus->rx_packet = hgicf_rx_data;
bus->tx_complete = hgicf_tx_complete;
bus->bus_priv = hg;
hg->tx_wq = ALLOC_ORDERED_WORKQUEUE("hgicf_tx", 4096);
if (!hg->tx_wq) {
goto __failed;
}
set_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags);
hgic_dbg("ok\n");
return 0;
}
__failed:
bus->rx_packet = NULL;
bus->tx_complete = NULL;
bus->bus_priv = NULL;
hgic_core_remove(hg);
hgic_dbg("fail\n");
return -1;
}
EXPORT_SYMBOL(hgic_core_probe);
#ifdef CONFIG_PM
int hgic_core_suspend(void *hgobj)
{
int err = 0;
struct hgicf_wdev *hg = (struct hgicf_wdev *)hgobj;
err = hgic_fwctrl_enter_sleep(&hg->ctrl, 1);
set_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags);
return err;
}
EXPORT_SYMBOL(hgic_core_suspend);
int hgic_core_resume(void *hgobj)
{
int err = 0;
struct hgicf_wdev *hg = (struct hgicf_wdev *)hgobj;
clear_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags);
if (hg->bus->reinit) {
hg->bus->reinit(hg->bus);
}
err = hgic_fwctrl_enter_sleep(&hg->ctrl, 0);
if(err)
hgic_err("exit sleep fail, ret=%d\r\n", err);
schedule_work(&hg->alive_work);
return err;
}
EXPORT_SYMBOL(hgic_core_resume);
#endif
void hgic_core_probe_post(void *priv)
{
struct hgicf_wdev *hg = (struct hgicf_wdev *)priv;
schedule_work(&hg->delay_init);
}
EXPORT_SYMBOL(hgic_core_probe_post);
#ifdef __RTOS__
int hgic_ifbus_reinit(const char *ifname)
{
struct hgicf_vif *vif = NULL;
struct net_device *ndev = net_device_get_by_name(ifname);
if (ndev) {
vif = (struct hgicf_vif *)netdev_priv(ndev);
if (vif->hg->bus->reinit) {
return vif->hg->bus->reinit(vif->hg->bus);
}
}
return 0;
}
struct hgic_ota *hgic_devota(struct net_device *dev)
{
struct hgicf_vif *vif = NULL;
if (dev == NULL) {
return NULL;
}
vif = (struct hgicf_vif *)netdev_priv(dev);
return &(vif->hg->ota);
}
#endif
int __init hgicf_init(void)
{
VERSOIN_SHOW("fmac");
#ifdef __RTOS__
rcu_init();
tasklet_core_init();
net_device_init();
#else
#ifdef CONFIG_HGIC_USBIN
hgic_usb_init();
#endif
#ifdef CONFIG_HGIC_SDIOIN
hgic_sdio_init();
#endif
#endif
hgic_leave();
return 0;
}
void __exit hgicf_exit(void)
{
#ifndef __RTOS__
#ifdef CONFIG_HGIC_USBIN
hgic_usb_exit();
#endif
#ifdef CONFIG_HGIC_SDIOIN
hgic_sdio_exit();
#endif
#endif
#ifdef __RTOS__
net_device_exit();
tasklet_core_exit();
#endif
}
const char *hgic_param_ifname(const char *name)
{
if (name) ifname = (char *)name;
return ifname;
}
#ifdef __RTOS__
void hgic_param_iftest(int iftest)
{
if_test = iftest;
}
char *hgic_param_fwfile(const char *fw)
{
if (fw) fw_file = fw;
return fw_file;
}
int hgic_param_ifcount(int count)
{
if (count) iface_cnt = count;
return iface_cnt;
}
void hgic_param_initcb(hgic_init_cb cb)
{
init_cb = cb;
}
void hgic_param_eventcb(hgic_event_cb cb)
{
event_cb = cb;
}
void hgic_param_bootdl(int enable)
{
no_bootdl = !enable;
}
#endif
void hgicf_event(struct hgicf_wdev *hg, char *name, int event, int param1, int param2)
{
#ifdef __RTOS__
if(event_cb)
event_cb(name, event, param1, param2);
#else
FWEVTQ_SET(&hg->evtq, event);
up(&hg->evtq.sema);
#endif
}
module_init(hgicf_init);
module_exit(hgicf_exit);
module_param(txq_size, int, 0);
module_param(fw_file, charp, 0644);
module_param(if_test, int, 0);
module_param(soft_fc, int, 0);
module_param(ifname, charp, 0644);
module_param(conf_file, charp, 0644);
module_param(no_bootdl, int, 0);
module_param(qc_mode, int, 0);
MODULE_DESCRIPTION("HUGE-IC FullMAC Wireless Card Driver");
MODULE_AUTHOR("Dongyun");
MODULE_LICENSE("GPL");