mirror of
https://github.com/torvalds/linux.git
synced 2026-06-08 22:52:35 +02:00
Update to 5.90.125.40
Add monitor interface support and fix cfg80211 management frame isses Add support for hostapd Use private command to get p2p device address Change-Id: Ie490e38f1af9f259ff4a96b2f7d367119c65c377 Signed-off-by: Howard M. Harte <hharte@broadcom.com> Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
This commit is contained in:
parent
b06f3b5edb
commit
2f66cb426e
|
|
@ -91,6 +91,7 @@ static histo_t vi_d1, vi_d2, vi_d3, vi_d4;
|
|||
|
||||
#if defined(SOFTAP)
|
||||
extern bool ap_cfg_running;
|
||||
extern bool ap_fw_loaded;
|
||||
#endif
|
||||
|
||||
/* enable HOSTIP cache update from the host side when an eth0:N is up */
|
||||
|
|
@ -442,6 +443,12 @@ static void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf);
|
|||
static void dhd_dump_htsfhisto(histo_t *his, char *s);
|
||||
#endif /* WLMEDIA_HTSF */
|
||||
|
||||
extern s32 wl_cfg80211_ifdel_ops(struct net_device *net);
|
||||
|
||||
/* Monitor interface */
|
||||
extern int dhd_monitor_init(void *dhd_pub);
|
||||
extern int dhd_monitor_uninit(void);
|
||||
|
||||
|
||||
#if defined(CONFIG_WIRELESS_EXT)
|
||||
struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
|
||||
|
|
@ -491,6 +498,7 @@ extern int register_pm_notifier(struct notifier_block *nb);
|
|||
extern int unregister_pm_notifier(struct notifier_block *nb);
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
|
||||
/* && defined(DHD_GPL) */
|
||||
|
||||
static void dhd_set_packet_filter(int value, dhd_pub_t *dhd)
|
||||
{
|
||||
#ifdef PKT_FILTER_SUPPORT
|
||||
|
|
@ -972,7 +980,7 @@ dhd_op_if(dhd_if_t *ifp)
|
|||
ret = -EOPNOTSUPP;
|
||||
} else {
|
||||
#if defined(SOFTAP)
|
||||
if (ap_cfg_running && !(dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) {
|
||||
if (ap_fw_loaded && !(dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) {
|
||||
/* semaphore that the soft AP CODE waits on */
|
||||
flags = dhd_os_spin_lock(&dhd->pub);
|
||||
|
||||
|
|
@ -992,6 +1000,9 @@ dhd_op_if(dhd_if_t *ifp)
|
|||
case WLC_E_IF_DEL:
|
||||
if (ifp->net != NULL) {
|
||||
DHD_TRACE(("\n%s: got 'WLC_E_IF_DEL' state\n", __FUNCTION__));
|
||||
#ifdef WL_CFG80211
|
||||
wl_cfg80211_ifdel_ops(ifp->net);
|
||||
#endif
|
||||
netif_stop_queue(ifp->net);
|
||||
unregister_netdev(ifp->net);
|
||||
ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */
|
||||
|
|
@ -1234,7 +1245,7 @@ dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -1462,7 +1473,6 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]);
|
||||
if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state)
|
||||
ifp = dhd->iflist[ifidx];
|
||||
|
|
@ -1473,6 +1483,14 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
|
|||
dhdp->dstats.rx_bytes += skb->len;
|
||||
dhdp->rx_packets++; /* Local count */
|
||||
|
||||
/* Dropping packets before registering net device to avoid kernel panic */
|
||||
if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) {
|
||||
DHD_ERROR(("%s: net device is NOT registered yet. drop [%s] packet\n",
|
||||
__FUNCTION__, (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) ? "event" : "data"));
|
||||
PKTFREE(dhdp->osh, pktbuf, TRUE);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_interrupt()) {
|
||||
netif_rx(skb);
|
||||
} else {
|
||||
|
|
@ -2363,7 +2381,6 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
|
|||
dhd_attach_states_t dhd_state = DHD_ATTACH_STATE_INIT;
|
||||
DHD_TRACE(("%s: Enter\n", __FUNCTION__));
|
||||
|
||||
|
||||
/* updates firmware nvram path if it was provided as module parameters */
|
||||
if ((firmware_path != NULL) && (firmware_path[0] != '\0'))
|
||||
strcpy(fw_path, firmware_path);
|
||||
|
|
@ -2469,6 +2486,8 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
|
|||
DHD_ERROR(("wl_cfg80211_attach failed\n"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dhd_monitor_init(&dhd->pub);
|
||||
dhd_state |= DHD_ATTACH_STATE_CFG80211;
|
||||
#endif
|
||||
#if defined(CONFIG_WIRELESS_EXT)
|
||||
|
|
@ -2563,7 +2582,6 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
|
|||
dhd_free(&dhd->pub);
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -2665,7 +2683,11 @@ dhd_bus_start(dhd_pub_t *dhdp)
|
|||
setbit(dhdp->eventmask, WLC_E_TXFAIL);
|
||||
setbit(dhdp->eventmask, WLC_E_JOIN_START);
|
||||
setbit(dhdp->eventmask, WLC_E_SCAN_COMPLETE);
|
||||
|
||||
setbit(dhdp->eventmask, WLC_E_ACTION_FRAME_RX);
|
||||
setbit(dhdp->eventmask, WLC_E_ACTION_FRAME_COMPLETE);
|
||||
#if defined(WLP2P)
|
||||
setbit(dhdp->eventmask, WLC_E_P2P_PROBREQ_MSG);
|
||||
#endif /* WLP2P */
|
||||
#ifdef PNO_SUPPORT
|
||||
setbit(dhdp->eventmask, WLC_E_PFN_NET_FOUND);
|
||||
#endif /* PNO_SUPPORT */
|
||||
|
|
@ -2694,10 +2716,6 @@ dhd_bus_start(dhd_pub_t *dhdp)
|
|||
dhd_write_macaddr(dhd->pub.mac.octet);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SYSCTL) && defined(WL_CFG80211)
|
||||
wl_cfg80211_sysctl_export_devaddr(&dhd->pub);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2724,6 +2742,7 @@ dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, in
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31))
|
||||
static struct net_device_ops dhd_ops_pri = {
|
||||
.ndo_open = dhd_open,
|
||||
|
|
@ -2848,6 +2867,8 @@ static int dhd_device_event(struct notifier_block *this,
|
|||
__FUNCTION__));
|
||||
aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, TRUE);
|
||||
}
|
||||
else
|
||||
aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, TRUE);
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
|
@ -2923,12 +2944,18 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
|
|||
* We have to use the primary MAC for virtual interfaces
|
||||
*/
|
||||
memcpy(temp_addr, dhd->iflist[ifidx]->mac_addr, ETHER_ADDR_LEN);
|
||||
if (ifidx == 1) {
|
||||
DHD_TRACE(("%s ACCESS POINT MAC: \n", __FUNCTION__));
|
||||
/* ACCESSPOINT INTERFACE CASE */
|
||||
temp_addr[0] |= 0x02; /* set bit 2 , - Locally Administered address */
|
||||
/*
|
||||
* Android sets the locally administered bit to indicate that this is a
|
||||
* portable hotspot. This will not work in simultaneous AP/STA mode,
|
||||
* nor with P2P. Need to set the Donlge's MAC address, and then use that.
|
||||
*/
|
||||
if (ifidx > 0) {
|
||||
DHD_ERROR(("%s interface [%s]: set locally administered bit in MAC\n",
|
||||
__func__, net->name));
|
||||
temp_addr[0] |= 0x02;
|
||||
}
|
||||
}
|
||||
|
||||
net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
|
||||
net->ethtool_ops = &dhd_ethtool_ops;
|
||||
|
|
@ -2951,11 +2978,11 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
|
|||
DHD_ERROR(("couldn't register the net device, err %d\n", err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
printf("%s: Driver up: Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
|
||||
printf("Broadcom Dongle Host Driver: register interface [%s]"
|
||||
" MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
|
||||
net->name,
|
||||
dhd->pub.mac.octet[0], dhd->pub.mac.octet[1], dhd->pub.mac.octet[2],
|
||||
dhd->pub.mac.octet[3], dhd->pub.mac.octet[4], dhd->pub.mac.octet[5]);
|
||||
net->dev_addr[0], net->dev_addr[1], net->dev_addr[2],
|
||||
net->dev_addr[3], net->dev_addr[4], net->dev_addr[5]);
|
||||
|
||||
#if defined(SOFTAP) && defined(CONFIG_WIRELESS_EXT) && !defined(WL_CFG80211)
|
||||
wl_iw_iscan_set_scan_broadcast_prep(net, 1);
|
||||
|
|
@ -3106,8 +3133,10 @@ void dhd_detach(dhd_pub_t *dhdp)
|
|||
}
|
||||
|
||||
#ifdef WL_CFG80211
|
||||
if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)
|
||||
if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) {
|
||||
wl_cfg80211_detach();
|
||||
dhd_monitor_uninit();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
|
||||
|
|
@ -3120,6 +3149,7 @@ void dhd_detach(dhd_pub_t *dhdp)
|
|||
wake_lock_destroy(&dhd->wl_rxwake);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
331
drivers/net/wireless/bcmdhd/dhd_linux_mon.c
Normal file
331
drivers/net/wireless/bcmdhd/dhd_linux_mon.c
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* Broadcom Dongle Host Driver (DHD), Linux monitor network interface
|
||||
*
|
||||
* Copyright (C) 1999-2011, Broadcom Corporation
|
||||
*
|
||||
* Unless you and Broadcom execute a separate written software license
|
||||
* agreement governing use of this software, this software is licensed to you
|
||||
* under the terms of the GNU General Public License version 2 (the "GPL"),
|
||||
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
|
||||
* following added to such license:
|
||||
*
|
||||
* As a special exception, the copyright holders of this software give you
|
||||
* permission to link this software with independent modules, and to copy and
|
||||
* distribute the resulting executable under terms of your choice, provided that
|
||||
* you also meet, for each linked independent module, the terms and conditions of
|
||||
* the license of that module. An independent module is a module which is not
|
||||
* derived from this software. The special exception does not apply to any
|
||||
* modifications of the software.
|
||||
*
|
||||
* Notwithstanding the above, under no circumstances may you combine this
|
||||
* software in any way with any other Broadcom software provided under a license
|
||||
* other than the GPL, without Broadcom's express prior written consent.
|
||||
*
|
||||
* $Id: dhd_linux_mon.c,v 1.131.2.55 2011-02-09 05:31:56 Exp $
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/ieee80211.h>
|
||||
#include <net/ieee80211_radiotap.h>
|
||||
|
||||
#include <wlioctl.h>
|
||||
#include <bcmutils.h>
|
||||
#include <linux_osl.h>
|
||||
#include <dhd_dbg.h>
|
||||
#include <dngl_stats.h>
|
||||
#include <dhd.h>
|
||||
|
||||
/*
|
||||
* Some external functions, TODO: move them to dhd_linux.h
|
||||
*/
|
||||
int dhd_start_xmit(struct sk_buff *skb, struct net_device *net);
|
||||
|
||||
/**
|
||||
* Local declarations and defintions (not exposed)
|
||||
*/
|
||||
#define MON_PRINT(format, ...) printk("DHD-MON: %s " format, __func__, ##__VA_ARGS__)
|
||||
#define MON_TRACE MON_PRINT
|
||||
|
||||
typedef struct monitor_interface {
|
||||
int radiotap_enabled;
|
||||
struct net_device* real_ndev; /* The real interface that the monitor is on */
|
||||
struct net_device* mon_ndev;
|
||||
} monitor_interface;
|
||||
|
||||
typedef struct dhd_linux_monitor {
|
||||
void *dhd_pub;
|
||||
monitor_interface mon_if[DHD_MAX_IFS];
|
||||
int count; /* Number of total monitor interface */
|
||||
struct mutex lock; /* lock to protect count and mon_if */
|
||||
} dhd_linux_monitor_t;
|
||||
|
||||
static dhd_linux_monitor_t g_monitor;
|
||||
|
||||
static struct net_device* lookup_real_netdev(char *name);
|
||||
static monitor_interface* ndev_to_monif(struct net_device *ndev);
|
||||
static int dhd_mon_if_open(struct net_device *ndev);
|
||||
static int dhd_mon_if_stop(struct net_device *ndev);
|
||||
static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev);
|
||||
static void dhd_mon_if_set_multicast_list(struct net_device *ndev);
|
||||
static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr);
|
||||
|
||||
static const struct net_device_ops dhd_mon_if_ops = {
|
||||
.ndo_open = dhd_mon_if_open,
|
||||
.ndo_stop = dhd_mon_if_stop,
|
||||
.ndo_start_xmit = dhd_mon_if_subif_start_xmit,
|
||||
.ndo_set_multicast_list = dhd_mon_if_set_multicast_list,
|
||||
.ndo_set_mac_address = dhd_mon_if_change_mac,
|
||||
};
|
||||
|
||||
/**
|
||||
* Local static function defintions
|
||||
*/
|
||||
|
||||
/* Look up dhd's net device table to find a match (e.g. interface "eth0" is a match for "mon.eth0"
|
||||
* "p2p-eth0-0" is a match for "mon.p2p-eth0-0")
|
||||
*/
|
||||
static struct net_device* lookup_real_netdev(char *name)
|
||||
{
|
||||
int i;
|
||||
int last_name_len = 0;
|
||||
struct net_device *ndev;
|
||||
struct net_device *ndev_found = NULL;
|
||||
|
||||
/* We want to find interface "p2p-eth0-0" for monitor interface "mon.p2p-eth0-0", so
|
||||
* we skip "eth0" even if "mon.p2p-eth0-0" contains "eth0"
|
||||
*/
|
||||
for (i = 0; i < DHD_MAX_IFS; i++) {
|
||||
ndev = dhd_idx2net(g_monitor.dhd_pub, i);
|
||||
if (ndev && strstr(name, ndev->name)) {
|
||||
if (strlen(ndev->name) > last_name_len)
|
||||
ndev_found = ndev;
|
||||
}
|
||||
}
|
||||
|
||||
return ndev_found;
|
||||
}
|
||||
|
||||
static monitor_interface* ndev_to_monif(struct net_device *ndev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DHD_MAX_IFS; i++) {
|
||||
if (g_monitor.mon_if[i].mon_ndev == ndev)
|
||||
return &g_monitor.mon_if[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int dhd_mon_if_open(struct net_device *ndev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
MON_PRINT("enter\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dhd_mon_if_stop(struct net_device *ndev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
MON_PRINT("enter\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
{
|
||||
int ret = 0;
|
||||
int rtap_len;
|
||||
int qos_len = 0;
|
||||
int dot11_hdr_len = 24;
|
||||
int snap_len = 6;
|
||||
unsigned char *pdata;
|
||||
unsigned char src_mac_addr[6];
|
||||
unsigned char dst_mac_addr[6];
|
||||
struct ieee80211_hdr *dot11_hdr;
|
||||
struct ieee80211_radiotap_header *rtap_hdr;
|
||||
monitor_interface* mon_if;
|
||||
|
||||
MON_PRINT("enter\n");
|
||||
|
||||
mon_if = ndev_to_monif(ndev);
|
||||
if (mon_if == NULL || mon_if->real_ndev == NULL) {
|
||||
MON_PRINT(" cannot find matched net dev, skip the packet\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
|
||||
goto fail;
|
||||
|
||||
rtap_hdr = (struct ieee80211_radiotap_header *)skb->data;
|
||||
if (unlikely(rtap_hdr->it_version))
|
||||
goto fail;
|
||||
|
||||
rtap_len = ieee80211_get_radiotap_len(skb->data);
|
||||
if (unlikely(skb->len < rtap_len))
|
||||
goto fail;
|
||||
|
||||
MON_PRINT("radiotap len (should be 14): %d\n", rtap_len);
|
||||
|
||||
/* Skip the ratio tap header */
|
||||
skb_pull(skb, rtap_len);
|
||||
|
||||
dot11_hdr = (struct ieee80211_hdr *)skb->data;
|
||||
|
||||
/* Check if the QoS bit is set */
|
||||
if (dot11_hdr->frame_control & 0x0080)
|
||||
qos_len = 2;
|
||||
|
||||
/* Check if this ia a Wireless Distribution System (WDS) frame
|
||||
* which has 4 MAC addresses
|
||||
*/
|
||||
if ((dot11_hdr->frame_control & 0x0300) == 0x0300)
|
||||
dot11_hdr_len += 6;
|
||||
|
||||
memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr));
|
||||
memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr));
|
||||
|
||||
/* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for
|
||||
* for two MAC addresses
|
||||
*/
|
||||
skb_pull(skb, dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2);
|
||||
pdata = (unsigned char*)skb->data;
|
||||
memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr));
|
||||
memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr, sizeof(src_mac_addr));
|
||||
|
||||
MON_PRINT("if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name);
|
||||
|
||||
/* Use the real net device to transmit the packet */
|
||||
ret = dhd_start_xmit(skb, mon_if->real_ndev);
|
||||
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
dev_kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dhd_mon_if_set_multicast_list(struct net_device *ndev)
|
||||
{
|
||||
monitor_interface* mon_if;
|
||||
|
||||
mon_if = ndev_to_monif(ndev);
|
||||
if (mon_if == NULL || mon_if->real_ndev == NULL) {
|
||||
MON_PRINT(" cannot find matched net dev, skip the packet\n");
|
||||
}
|
||||
|
||||
MON_PRINT("enter, if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name);
|
||||
}
|
||||
|
||||
static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr)
|
||||
{
|
||||
int ret = 0;
|
||||
monitor_interface* mon_if;
|
||||
|
||||
mon_if = ndev_to_monif(ndev);
|
||||
if (mon_if == NULL || mon_if->real_ndev == NULL) {
|
||||
MON_PRINT(" cannot find matched net dev, skip the packet\n");
|
||||
}
|
||||
|
||||
MON_PRINT("enter, if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Global function definitions (declared in dhd_linux_mon.h)
|
||||
*/
|
||||
|
||||
int dhd_add_monitor(char *name, struct net_device **new_ndev)
|
||||
{
|
||||
int idx;
|
||||
int ret = 0;
|
||||
struct net_device* ndev = NULL;
|
||||
dhd_linux_monitor_t **dhd_mon;
|
||||
|
||||
mutex_lock(&g_monitor.lock);
|
||||
|
||||
MON_TRACE("enter, if name: %s\n", name);
|
||||
if (!name || !new_ndev) {
|
||||
MON_PRINT("invalid parameters\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (g_monitor.count >= DHD_MAX_IFS) {
|
||||
MON_PRINT("exceeds maximum interfaces\n");
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ndev = alloc_etherdev(sizeof(dhd_linux_monitor_t*));
|
||||
if (!ndev) {
|
||||
MON_PRINT("failed to allocate memory\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ndev->type = ARPHRD_IEEE80211_RADIOTAP;
|
||||
strncpy(ndev->name, name, IFNAMSIZ);
|
||||
ndev->name[IFNAMSIZ - 1] = 0;
|
||||
ndev->netdev_ops = &dhd_mon_if_ops;
|
||||
|
||||
ret = register_netdevice(ndev);
|
||||
if (ret) {
|
||||
MON_PRINT(" register_netdevice failed (%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*new_ndev = ndev;
|
||||
idx = g_monitor.count;
|
||||
g_monitor.mon_if[idx].radiotap_enabled = TRUE;
|
||||
g_monitor.mon_if[idx].mon_ndev = ndev;
|
||||
g_monitor.mon_if[idx].real_ndev = lookup_real_netdev(name);
|
||||
g_monitor.count++;
|
||||
dhd_mon = (dhd_linux_monitor_t **)netdev_priv(ndev);
|
||||
*dhd_mon = &g_monitor;
|
||||
|
||||
MON_PRINT("net device returned: 0x%p\n", ndev);
|
||||
MON_PRINT("found a matched net device, name %s\n", g_monitor.mon_if[idx].real_ndev->name);
|
||||
|
||||
out:
|
||||
if (ret && ndev)
|
||||
free_netdev(ndev);
|
||||
|
||||
mutex_unlock(&g_monitor.lock);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int dhd_monitor_init(void *dhd_pub)
|
||||
{
|
||||
g_monitor.dhd_pub = dhd_pub;
|
||||
mutex_init(&g_monitor.lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhd_monitor_uninit(void)
|
||||
{
|
||||
int i;
|
||||
struct net_device *ndev;
|
||||
|
||||
mutex_lock(&g_monitor.lock);
|
||||
|
||||
for (i = 0; i < DHD_MAX_IFS; i++) {
|
||||
ndev = g_monitor.mon_if[i].mon_ndev;
|
||||
if (ndev) {
|
||||
unregister_netdev(ndev);
|
||||
free_netdev(ndev);
|
||||
g_monitor.mon_if[i].real_ndev = NULL;
|
||||
g_monitor.mon_if[i].mon_ndev = NULL;
|
||||
g_monitor.count--;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&g_monitor.lock);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -392,6 +392,11 @@ static bool dhd_readahead;
|
|||
|
||||
/* To check if there's window offered */
|
||||
#define DATAOK(bus) \
|
||||
(((uint8)(bus->tx_max - bus->tx_seq) > 1) && \
|
||||
(((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
|
||||
|
||||
/* To check if there's window offered for ctrl frame*/
|
||||
#define TXCTLOK(bus) \
|
||||
(((uint8)(bus->tx_max - bus->tx_seq) != 0) && \
|
||||
(((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
|
||||
|
||||
|
|
@ -1364,7 +1369,7 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen)
|
|||
htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN);
|
||||
htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
|
||||
|
||||
if (!DATAOK(bus)) {
|
||||
if (!TXCTLOK(bus)) {
|
||||
DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n",
|
||||
__FUNCTION__, bus->tx_max, bus->tx_seq));
|
||||
bus->ctrl_frame_stat = TRUE;
|
||||
|
|
@ -2602,46 +2607,44 @@ dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const ch
|
|||
|
||||
break;
|
||||
#ifdef SOFTAP
|
||||
case IOV_GVAL(IOV_FWPATH):
|
||||
{
|
||||
uint32 fw_path_len;
|
||||
|
||||
fw_path_len = strlen(bus->fw_path);
|
||||
|
||||
DHD_INFO(("[softap] get fwpath, l=%d\n", len));
|
||||
|
||||
if ( fw_path_len > len-1 ) {
|
||||
bcmerror = BCME_BUFTOOSHORT;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( fw_path_len )
|
||||
bcopy(bus->fw_path, arg, fw_path_len);
|
||||
|
||||
((uchar*)arg)[fw_path_len] = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case IOV_SVAL(IOV_FWPATH):
|
||||
{
|
||||
DHD_INFO(("[softap] set fwpath, idx=%d\n", int_val));
|
||||
|
||||
switch(int_val) {
|
||||
case 1:
|
||||
bus->fw_path = fw_path; /* ordinary one */
|
||||
break;
|
||||
case 2:
|
||||
bus->fw_path = fw_path2;
|
||||
break;
|
||||
default:
|
||||
bcmerror = BCME_BADARG;
|
||||
return bcmerror;
|
||||
}
|
||||
|
||||
DHD_INFO(("[softap] new fw path: %s\n", (bus->fw_path[0]?bus->fw_path:"NULL")));
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case IOV_GVAL(IOV_FWPATH):
|
||||
{
|
||||
uint32 fw_path_len;
|
||||
|
||||
fw_path_len = strlen(bus->fw_path);
|
||||
DHD_INFO(("[softap] get fwpath, l=%d\n", len));
|
||||
|
||||
if (fw_path_len > len-1) {
|
||||
bcmerror = BCME_BUFTOOSHORT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fw_path_len) {
|
||||
bcopy(bus->fw_path, arg, fw_path_len);
|
||||
((uchar*)arg)[fw_path_len] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case IOV_SVAL(IOV_FWPATH):
|
||||
DHD_INFO(("[softap] set fwpath, idx=%d\n", int_val));
|
||||
|
||||
switch (int_val) {
|
||||
case 1:
|
||||
bus->fw_path = fw_path; /* ordinary one */
|
||||
break;
|
||||
case 2:
|
||||
bus->fw_path = fw_path2;
|
||||
break;
|
||||
default:
|
||||
bcmerror = BCME_BADARG;
|
||||
break;
|
||||
}
|
||||
|
||||
DHD_INFO(("[softap] new fw path: %s\n", (bus->fw_path[0] ? bus->fw_path : "NULL")));
|
||||
break;
|
||||
|
||||
#endif /* SOFTAP */
|
||||
case IOV_GVAL(IOV_DEVRESET):
|
||||
DHD_TRACE(("%s: Called get IOV_DEVRESET\n", __FUNCTION__));
|
||||
|
||||
|
|
@ -4386,6 +4389,12 @@ dhdsdio_dpc(dhd_bus_t *bus)
|
|||
|
||||
DHD_TRACE(("%s: Enter\n", __FUNCTION__));
|
||||
|
||||
if (bus->dhd->busstate == DHD_BUS_DOWN) {
|
||||
DHD_ERROR(("%s: Bus down, ret\n", __FUNCTION__));
|
||||
bus->intstatus = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Start with leftover status bits */
|
||||
intstatus = bus->intstatus;
|
||||
|
||||
|
|
@ -4544,7 +4553,7 @@ dhdsdio_dpc(dhd_bus_t *bus)
|
|||
bcmsdh_intr_enable(sdh);
|
||||
}
|
||||
|
||||
if (DATAOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) {
|
||||
if (TXCTLOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) {
|
||||
int ret, i;
|
||||
|
||||
ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
|
||||
|
|
@ -4589,6 +4598,9 @@ dhdsdio_dpc(dhd_bus_t *bus)
|
|||
framecnt = dhdsdio_sendfromq(bus, framecnt);
|
||||
txlimit -= framecnt;
|
||||
}
|
||||
/* Resched the DPC if ctrl cmd is pending on bus credit*/
|
||||
if (bus->ctrl_frame_stat)
|
||||
resched = TRUE;
|
||||
|
||||
/* Resched if events or tx frames are pending, else await next interrupt */
|
||||
/* On failed register access, all bets are off: no resched or interrupts */
|
||||
|
|
@ -5176,7 +5188,7 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot,
|
|||
sd1idle = TRUE;
|
||||
dhd_readahead = TRUE;
|
||||
retrydata = FALSE;
|
||||
dhd_doflow = TRUE;
|
||||
dhd_doflow = FALSE;
|
||||
dhd_dongle_memsize = 0;
|
||||
dhd_txminmax = DHD_TXMINMAX;
|
||||
|
||||
|
|
@ -5291,15 +5303,20 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/* Register interrupt callback, but mask it (not operational yet). */
|
||||
DHD_INTR(("%s: disable SDIO interrupts (not interested yet)\n", __FUNCTION__));
|
||||
bcmsdh_intr_disable(sdh);
|
||||
if ((ret = bcmsdh_intr_reg(sdh, dhdsdio_isr, bus)) != 0) {
|
||||
DHD_ERROR(("%s: FAILED: bcmsdh_intr_reg returned %d\n",
|
||||
__FUNCTION__, ret));
|
||||
goto fail;
|
||||
if (bus->intr) {
|
||||
/* Register interrupt callback, but mask it (not operational yet). */
|
||||
DHD_INTR(("%s: disable SDIO interrupts (not interested yet)\n", __FUNCTION__));
|
||||
bcmsdh_intr_disable(sdh);
|
||||
if ((ret = bcmsdh_intr_reg(sdh, dhdsdio_isr, bus)) != 0) {
|
||||
DHD_ERROR(("%s: FAILED: bcmsdh_intr_reg returned %d\n",
|
||||
__FUNCTION__, ret));
|
||||
goto fail;
|
||||
}
|
||||
DHD_INTR(("%s: registered SDIO interrupt function ok\n", __FUNCTION__));
|
||||
} else {
|
||||
DHD_INFO(("%s: SDIO interrupt function is NOT registered due to polling mode\n",
|
||||
__FUNCTION__));
|
||||
}
|
||||
DHD_INTR(("%s: registered SDIO interrupt function ok\n", __FUNCTION__));
|
||||
|
||||
DHD_INFO(("%s: completed!!\n", __FUNCTION__));
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#define CMD_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP"
|
||||
#define CMD_BTCOEXMODE "BTCOEXMODE"
|
||||
#define CMD_SETSUSPENDOPT "SETSUSPENDOPT"
|
||||
#define CMD_P2P_DEV_ADDR "P2P_DEV_ADDR"
|
||||
#define CMD_SETFWPATH "SETFWPATH"
|
||||
#define CMD_SETBAND "SETBAND"
|
||||
#define CMD_GETBAND "GETBAND"
|
||||
|
|
@ -105,6 +106,7 @@ typedef struct android_wifi_priv_cmd {
|
|||
void dhd_customer_gpio_wlan_ctrl(int onoff);
|
||||
uint dhd_dev_reset(struct net_device *dev, uint8 flag);
|
||||
void dhd_dev_init_ioctl(struct net_device *dev);
|
||||
int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr);
|
||||
|
||||
extern bool ap_fw_loaded;
|
||||
#ifdef CUSTOMER_HW2
|
||||
|
|
@ -276,6 +278,18 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t
|
|||
}
|
||||
#endif
|
||||
|
||||
static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len)
|
||||
{
|
||||
int ret;
|
||||
int bytes_written = 0;
|
||||
|
||||
ret = wl_cfg80211_get_p2p_dev_addr(ndev, (struct ether_addr*)command);
|
||||
if (ret)
|
||||
return 0;
|
||||
bytes_written = sizeof(struct ether_addr);
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
/**
|
||||
* Global function definitions (declared in wl_android.h)
|
||||
*/
|
||||
|
|
@ -439,8 +453,8 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
|
|||
else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) {
|
||||
char *country_code = command + strlen(CMD_COUNTRY) + 1;
|
||||
bytes_written = wldev_set_country(net, country_code);
|
||||
#ifdef PNO_SUPPORT
|
||||
}
|
||||
#ifdef PNO_SUPPORT
|
||||
else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) {
|
||||
bytes_written = dhd_dev_pno_reset(net);
|
||||
}
|
||||
|
|
@ -450,7 +464,10 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
|
|||
else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) {
|
||||
uint pfn_enabled = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0';
|
||||
bytes_written = dhd_dev_pno_enable(net, pfn_enabled);
|
||||
}
|
||||
#endif
|
||||
else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) {
|
||||
bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd->total_len);
|
||||
} else {
|
||||
DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command));
|
||||
snprintf(command, 3, "OK");
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -114,12 +114,6 @@ do { \
|
|||
*/
|
||||
#define WL_FILE_NAME_MAX 256
|
||||
#define WL_DWELL_TIME 200
|
||||
|
||||
/* WiFi Direct */
|
||||
#define WL_P2P_WILDCARD_SSID "DIRECT-"
|
||||
#define WL_P2P_WILDCARD_SSID_LEN 7
|
||||
#define WL_P2P_INTERFACE_PREFIX "p2p"
|
||||
#define WL_P2P_TEMP_CHAN "11"
|
||||
#define VWDEV_CNT 3
|
||||
/* dongle status */
|
||||
enum wl_status {
|
||||
|
|
@ -127,7 +121,9 @@ enum wl_status {
|
|||
WL_STATUS_SCANNING,
|
||||
WL_STATUS_SCAN_ABORTING,
|
||||
WL_STATUS_CONNECTING,
|
||||
WL_STATUS_CONNECTED
|
||||
WL_STATUS_CONNECTED,
|
||||
WL_STATUS_AP_CREATING,
|
||||
WL_STATUS_AP_CREATED
|
||||
};
|
||||
|
||||
/* wi-fi mode */
|
||||
|
|
@ -315,6 +311,17 @@ struct escan_info {
|
|||
struct wiphy *wiphy;
|
||||
};
|
||||
|
||||
struct ap_info {
|
||||
/* Structure to hold WPS, WPA IEs for a AP */
|
||||
u8 probe_res_ie[IE_MAX_LEN];
|
||||
u8 beacon_ie[IE_MAX_LEN];
|
||||
u32 probe_res_ie_len;
|
||||
u32 beacon_ie_len;
|
||||
u8 *wpa_ie;
|
||||
u8 *rsn_ie;
|
||||
u8 *wps_ie;
|
||||
bool security_mode;
|
||||
};
|
||||
/* dongle private data of cfg80211 interface */
|
||||
struct wl_priv {
|
||||
struct wireless_dev *wdev; /* representing wl cfg80211 device */
|
||||
|
|
@ -373,12 +380,13 @@ struct wl_priv {
|
|||
struct dentry *debugfsdir;
|
||||
struct rfkill *rfkill;
|
||||
bool rf_blocked;
|
||||
|
||||
struct ieee80211_channel remain_on_chan;
|
||||
enum nl80211_channel_type remain_on_chan_type;
|
||||
u64 cache_cookie;
|
||||
wait_queue_head_t dongle_event_wait;
|
||||
struct p2p_info p2p;
|
||||
struct ap_info *ap_info;
|
||||
struct p2p_info *p2p;
|
||||
bool p2p_supported;
|
||||
s8 last_eventmask[WL_EVENTING_MASK_LEN];
|
||||
u8 ci[0] __attribute__ ((__aligned__(NETDEV_ALIGN)));
|
||||
};
|
||||
|
|
@ -398,6 +406,10 @@ struct wl_priv {
|
|||
#define wl_to_iscan(w) (w->iscan)
|
||||
#define wl_to_conn(w) (&w->conn_info)
|
||||
#define wiphy_from_scan(w) (w->escan_info.wiphy)
|
||||
#define wl_get_drv_status(wl, stat) (test_bit(WL_STATUS_ ## stat, &(wl)->status))
|
||||
#define wl_set_drv_status(wl, stat) (set_bit(WL_STATUS_ ## stat, &(wl)->status))
|
||||
#define wl_clr_drv_status(wl, stat) (clear_bit(WL_STATUS_ ## stat, &(wl)->status))
|
||||
#define wl_chg_drv_status(wl, stat) (change_bit(WL_STATUS_ ## stat, &(wl)->status))
|
||||
|
||||
static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss)
|
||||
{
|
||||
|
|
@ -488,6 +500,7 @@ extern struct sdio_func *wl_cfg80211_get_sdio_func(void); /* set sdio function i
|
|||
extern s32 wl_cfg80211_up(void); /* dongle up */
|
||||
extern s32 wl_cfg80211_down(void); /* dongle down */
|
||||
extern s32 wl_cfg80211_notify_ifadd(struct net_device *net);
|
||||
extern s32 wl_cfg80211_ifdel_ops(struct net_device *net);
|
||||
extern s32 wl_cfg80211_notify_ifdel(struct net_device *net);
|
||||
extern s32 wl_cfg80211_is_progress_ifadd(void);
|
||||
extern s32 wl_cfg80211_is_progress_ifchange(void);
|
||||
|
|
@ -499,6 +512,7 @@ extern s32 wl_cfg80211_read_fw(s8 *buf, u32 size);
|
|||
extern void wl_cfg80211_release_fw(void);
|
||||
extern s8 *wl_cfg80211_get_fwname(void);
|
||||
extern s8 *wl_cfg80211_get_nvramname(void);
|
||||
extern s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr);
|
||||
#ifdef CONFIG_SYSCTL
|
||||
extern s32 wl_cfg80211_sysctl_export_devaddr(void *data);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -65,14 +65,13 @@ wl_cfgp2p_vndr_ie(struct net_device *ndev, s32 bssidx, s32 pktflag,
|
|||
* Initialize variables related to P2P
|
||||
*
|
||||
*/
|
||||
void
|
||||
s32
|
||||
wl_cfgp2p_init_priv(struct wl_priv *wl)
|
||||
{
|
||||
wl->p2p.on = 0;
|
||||
wl->p2p.scan = 0; /* by default , legacy scan */
|
||||
wl->p2p.status = 0;
|
||||
wl->p2p.listen_timer = NULL;
|
||||
|
||||
if (!(wl->p2p = kzalloc(sizeof(struct p2p_info), GFP_KERNEL))) {
|
||||
CFGP2P_ERR(("struct p2p_info allocation failed\n"));
|
||||
return -ENOMEM;
|
||||
}
|
||||
#define INIT_IE(IE_TYPE, BSS_TYPE) \
|
||||
do { \
|
||||
memset(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie, 0, \
|
||||
|
|
@ -103,17 +102,38 @@ wl_cfgp2p_init_priv(struct wl_priv *wl)
|
|||
wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = NULL;
|
||||
wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = 0;
|
||||
|
||||
}
|
||||
return BCME_OK;
|
||||
|
||||
}
|
||||
/*
|
||||
* Deinitialize variables related to P2P
|
||||
*
|
||||
*/
|
||||
void
|
||||
wl_cfgp2p_deinit_priv(struct wl_priv *wl)
|
||||
{
|
||||
if (wl->p2p) {
|
||||
kfree(wl->p2p);
|
||||
}
|
||||
wl->p2p_supported = 0;
|
||||
}
|
||||
/*
|
||||
* Set P2P functions into firmware
|
||||
*/
|
||||
s32
|
||||
wl_cfgp2p_set_firm_p2p(struct wl_priv *wl)
|
||||
{
|
||||
struct net_device *ndev = wl_to_prmry_ndev(wl);
|
||||
s32 ret = BCME_OK;
|
||||
|
||||
/* TODO : Do we have to check whether APSTA is enabled or not ? */
|
||||
s32 val = 0;
|
||||
/* Do we have to check whether APSTA is enabled or not ? */
|
||||
wldev_iovar_getint(ndev, "apsta", &val);
|
||||
if (val == 0) {
|
||||
val = 1;
|
||||
wldev_ioctl(ndev, WLC_DOWN, &val, sizeof(s32), false);
|
||||
wldev_iovar_setint(ndev, "apsta", val);
|
||||
wldev_ioctl(ndev, WLC_UP, &val, sizeof(s32), false);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +150,7 @@ wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type,
|
|||
{
|
||||
wl_p2p_if_t ifreq;
|
||||
s32 err;
|
||||
struct net_device *netdev = wl_to_prmry_ndev(wl);
|
||||
struct net_device *ndev = wl_to_prmry_ndev(wl);
|
||||
|
||||
ifreq.type = if_type;
|
||||
ifreq.chspec = chspec;
|
||||
|
|
@ -142,7 +162,7 @@ wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type,
|
|||
(if_type == WL_P2P_IF_GO) ? "go" : "client",
|
||||
(chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT));
|
||||
|
||||
err = wldev_iovar_setbuf(netdev, "p2p_ifadd", &ifreq, sizeof(ifreq),
|
||||
err = wldev_iovar_setbuf(ndev, "p2p_ifadd", &ifreq, sizeof(ifreq),
|
||||
ioctlbuf, sizeof(ioctlbuf));
|
||||
return err;
|
||||
}
|
||||
|
|
@ -571,21 +591,21 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active,
|
|||
/* Check whether the given IE looks like WFA P2P IE. */
|
||||
#define wl_cfgp2p_is_p2p_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \
|
||||
(const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_P2P)
|
||||
/* Delete and Set a management ie to firmware
|
||||
/* Delete and Set a management vndr ie to firmware
|
||||
* Parameters:
|
||||
* @wl : wl_private data
|
||||
* @ndev : net device for bssidx
|
||||
* @bssidx : bssidx for BSS
|
||||
* @pktflag : packet flag for IE (VNDR_IE_PRBREQ_FLAG,VNDR_IE_PRBRSP_FLAG, VNDR_IE_ASSOCRSP_FLAG,
|
||||
* VNDR_IE_ASSOCREQ_FLAG)
|
||||
* @ie : probe request ie (WPS IE + P2P IE)
|
||||
* @ie_len : probe request ie length
|
||||
* @ie : VNDR IE (such as P2P IE , WPS IE)
|
||||
* @ie_len : VNDR IE Length
|
||||
* Returns 0 if success.
|
||||
*/
|
||||
|
||||
s32
|
||||
wl_cfgp2p_set_managment_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx,
|
||||
s32 pktflag, const u8 *p2p_ie, u32 p2p_ie_len)
|
||||
s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len)
|
||||
{
|
||||
/* Vendor-specific Information Element ID */
|
||||
#define VNDR_SPEC_ELEMENT_ID 0xdd
|
||||
|
|
@ -601,50 +621,70 @@ wl_cfgp2p_set_managment_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssi
|
|||
#define IE_TYPE_LEN(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie_len)
|
||||
if (bssidx == -1)
|
||||
return BCME_BADARG;
|
||||
if (bssidx == P2PAPI_BSSCFG_PRIMARY)
|
||||
bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE);
|
||||
switch (pktflag) {
|
||||
case VNDR_IE_PRBREQ_FLAG :
|
||||
mgmt_ie_buf = IE_TYPE(probe_req, bssidx);
|
||||
mgmt_ie_len = &IE_TYPE_LEN(probe_req, bssidx);
|
||||
mgmt_ie_buf_len = sizeof(IE_TYPE(probe_req, bssidx));
|
||||
break;
|
||||
case VNDR_IE_PRBRSP_FLAG :
|
||||
mgmt_ie_buf = IE_TYPE(probe_res, bssidx);
|
||||
mgmt_ie_len = &IE_TYPE_LEN(probe_res, bssidx);
|
||||
mgmt_ie_buf_len = sizeof(IE_TYPE(probe_res, bssidx));
|
||||
break;
|
||||
case VNDR_IE_ASSOCREQ_FLAG :
|
||||
mgmt_ie_buf = IE_TYPE(assoc_req, bssidx);
|
||||
mgmt_ie_len = &IE_TYPE_LEN(assoc_req, bssidx);
|
||||
mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_req, bssidx));
|
||||
break;
|
||||
case VNDR_IE_ASSOCRSP_FLAG :
|
||||
mgmt_ie_buf = IE_TYPE(assoc_res, bssidx);
|
||||
mgmt_ie_len = &IE_TYPE_LEN(assoc_res, bssidx);
|
||||
mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_res, bssidx));
|
||||
break;
|
||||
case VNDR_IE_BEACON_FLAG :
|
||||
mgmt_ie_buf = IE_TYPE(beacon, bssidx);
|
||||
mgmt_ie_len = &IE_TYPE_LEN(beacon, bssidx);
|
||||
mgmt_ie_buf_len = sizeof(IE_TYPE(beacon, bssidx));
|
||||
break;
|
||||
default:
|
||||
mgmt_ie_buf = NULL;
|
||||
mgmt_ie_len = NULL;
|
||||
CFGP2P_ERR(("not suitable type\n"));
|
||||
return -1;
|
||||
if (wl->p2p_supported && p2p_on(wl)) {
|
||||
if (bssidx == P2PAPI_BSSCFG_PRIMARY)
|
||||
bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE);
|
||||
switch (pktflag) {
|
||||
case VNDR_IE_PRBREQ_FLAG :
|
||||
mgmt_ie_buf = IE_TYPE(probe_req, bssidx);
|
||||
mgmt_ie_len = &IE_TYPE_LEN(probe_req, bssidx);
|
||||
mgmt_ie_buf_len = sizeof(IE_TYPE(probe_req, bssidx));
|
||||
break;
|
||||
case VNDR_IE_PRBRSP_FLAG :
|
||||
mgmt_ie_buf = IE_TYPE(probe_res, bssidx);
|
||||
mgmt_ie_len = &IE_TYPE_LEN(probe_res, bssidx);
|
||||
mgmt_ie_buf_len = sizeof(IE_TYPE(probe_res, bssidx));
|
||||
break;
|
||||
case VNDR_IE_ASSOCREQ_FLAG :
|
||||
mgmt_ie_buf = IE_TYPE(assoc_req, bssidx);
|
||||
mgmt_ie_len = &IE_TYPE_LEN(assoc_req, bssidx);
|
||||
mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_req, bssidx));
|
||||
break;
|
||||
case VNDR_IE_ASSOCRSP_FLAG :
|
||||
mgmt_ie_buf = IE_TYPE(assoc_res, bssidx);
|
||||
mgmt_ie_len = &IE_TYPE_LEN(assoc_res, bssidx);
|
||||
mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_res, bssidx));
|
||||
break;
|
||||
case VNDR_IE_BEACON_FLAG :
|
||||
mgmt_ie_buf = IE_TYPE(beacon, bssidx);
|
||||
mgmt_ie_len = &IE_TYPE_LEN(beacon, bssidx);
|
||||
mgmt_ie_buf_len = sizeof(IE_TYPE(beacon, bssidx));
|
||||
break;
|
||||
default:
|
||||
mgmt_ie_buf = NULL;
|
||||
mgmt_ie_len = NULL;
|
||||
CFGP2P_ERR(("not suitable type\n"));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
switch (pktflag) {
|
||||
case VNDR_IE_PRBRSP_FLAG :
|
||||
mgmt_ie_buf = wl->ap_info->probe_res_ie;
|
||||
mgmt_ie_len = &wl->ap_info->probe_res_ie_len;
|
||||
mgmt_ie_buf_len = sizeof(wl->ap_info->probe_res_ie);
|
||||
break;
|
||||
case VNDR_IE_BEACON_FLAG :
|
||||
mgmt_ie_buf = wl->ap_info->beacon_ie;
|
||||
mgmt_ie_len = &wl->ap_info->beacon_ie_len;
|
||||
mgmt_ie_buf_len = sizeof(wl->ap_info->beacon_ie);
|
||||
break;
|
||||
default:
|
||||
mgmt_ie_buf = NULL;
|
||||
mgmt_ie_len = NULL;
|
||||
CFGP2P_ERR(("not suitable type\n"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* Add if there is any extra IE */
|
||||
if (p2p_ie && p2p_ie_len) {
|
||||
CFGP2P_INFO(("Request has extra IE"));
|
||||
if (p2p_ie_len > mgmt_ie_buf_len) {
|
||||
if (vndr_ie && vndr_ie_len) {
|
||||
CFGP2P_ERR(("Request has extra IE"));
|
||||
if (vndr_ie_len > mgmt_ie_buf_len) {
|
||||
CFGP2P_ERR(("extra IE size too big\n"));
|
||||
ret = -ENOMEM;
|
||||
} else {
|
||||
if (mgmt_ie_buf != NULL) {
|
||||
if ((p2p_ie_len == *mgmt_ie_len) &&
|
||||
(memcmp(mgmt_ie_buf, p2p_ie, p2p_ie_len) == 0)) {
|
||||
if ((vndr_ie_len == *mgmt_ie_len) &&
|
||||
(memcmp(mgmt_ie_buf, vndr_ie, vndr_ie_len) == 0)) {
|
||||
CFGP2P_INFO(("Previous mgmt IE is equals to current IE"));
|
||||
goto exit;
|
||||
}
|
||||
|
|
@ -666,12 +706,12 @@ wl_cfgp2p_set_managment_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssi
|
|||
|
||||
}
|
||||
/* save the current IE in wl struct */
|
||||
memcpy(mgmt_ie_buf, p2p_ie, p2p_ie_len);
|
||||
*mgmt_ie_len = p2p_ie_len;
|
||||
memcpy(mgmt_ie_buf, vndr_ie, vndr_ie_len);
|
||||
*mgmt_ie_len = vndr_ie_len;
|
||||
pos = 0;
|
||||
ie_buf = (u8 *) p2p_ie;
|
||||
ie_buf = (u8 *) vndr_ie;
|
||||
delete = 0;
|
||||
while (pos < p2p_ie_len) {
|
||||
while (pos < vndr_ie_len) {
|
||||
ie_id = ie_buf[pos++];
|
||||
ie_len = ie_buf[pos++];
|
||||
if ((ie_id == DOT11_MNG_VS_ID) &&
|
||||
|
|
@ -719,8 +759,6 @@ wl_cfgp2p_clear_management_ie(struct wl_priv *wl, s32 bssidx)
|
|||
INIT_IE(assoc_req, bssidx);
|
||||
INIT_IE(assoc_res, bssidx);
|
||||
INIT_IE(beacon, bssidx);
|
||||
|
||||
|
||||
return BCME_OK;
|
||||
}
|
||||
|
||||
|
|
@ -749,6 +787,7 @@ wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u
|
|||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wpa_ie_fixed_t *
|
||||
wl_cfgp2p_find_wpaie(u8 *parse, u32 len)
|
||||
{
|
||||
|
|
@ -855,12 +894,17 @@ wl_cfgp2p_find_idx(struct wl_priv *wl, struct net_device *ndev)
|
|||
CFGP2P_ERR((" ndev is NULL\n"));
|
||||
goto exit;
|
||||
}
|
||||
if (!wl->p2p_supported) {
|
||||
return P2PAPI_BSSCFG_PRIMARY;
|
||||
}
|
||||
for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) {
|
||||
if (ndev == wl_to_p2p_bss_ndev(wl, i)) {
|
||||
index = wl_to_p2p_bss_bssidx(wl, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == -1)
|
||||
return P2PAPI_BSSCFG_PRIMARY;
|
||||
exit:
|
||||
return index;
|
||||
}
|
||||
|
|
@ -874,12 +918,11 @@ wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev,
|
|||
s32 ret = BCME_OK;
|
||||
|
||||
CFGP2P_DBG((" Enter\n"));
|
||||
/* TODO : have to acquire bottom half lock ? */
|
||||
if (wl_get_p2p_status(wl, LISTEN_EXPIRED) == 0) {
|
||||
wl_set_p2p_status(wl, LISTEN_EXPIRED);
|
||||
|
||||
if (wl->p2p.listen_timer)
|
||||
del_timer_sync(wl->p2p.listen_timer);
|
||||
if (wl->p2p->listen_timer)
|
||||
del_timer_sync(wl->p2p->listen_timer);
|
||||
|
||||
cfg80211_remain_on_channel_expired(ndev, wl->cache_cookie, &wl->remain_on_chan,
|
||||
wl->remain_on_chan_type, GFP_KERNEL);
|
||||
|
|
@ -944,12 +987,12 @@ wl_cfgp2p_discover_listen(struct wl_priv *wl, s32 channel, u32 duration_ms)
|
|||
wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_LISTEN, channel, (u16) duration_ms,
|
||||
wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE));
|
||||
|
||||
if (wl->p2p.listen_timer)
|
||||
del_timer_sync(wl->p2p.listen_timer);
|
||||
if (wl->p2p->listen_timer)
|
||||
del_timer_sync(wl->p2p->listen_timer);
|
||||
|
||||
wl->p2p.listen_timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
|
||||
wl->p2p->listen_timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
|
||||
|
||||
if (wl->p2p.listen_timer == NULL) {
|
||||
if (wl->p2p->listen_timer == NULL) {
|
||||
CFGP2P_ERR(("listen_timer allocation failed\n"));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -957,7 +1000,7 @@ wl_cfgp2p_discover_listen(struct wl_priv *wl, s32 channel, u32 duration_ms)
|
|||
/* We will wait to receive WLC_E_P2P_DISC_LISTEN_COMPLETE from dongle ,
|
||||
* otherwise we will wait up to duration_ms + 10ms
|
||||
*/
|
||||
INIT_TIMER(wl->p2p.listen_timer, wl_cfgp2p_listen_expired, duration_ms, 20);
|
||||
INIT_TIMER(wl->p2p->listen_timer, wl_cfgp2p_listen_expired, duration_ms, 20);
|
||||
|
||||
#undef INIT_TIMER
|
||||
exit:
|
||||
|
|
@ -986,7 +1029,7 @@ wl_cfgp2p_discover_enable_search(struct wl_priv *wl, u8 enable)
|
|||
*/
|
||||
if (!enable) {
|
||||
wl_clr_p2p_status(wl, SCANNING);
|
||||
(void) wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0,
|
||||
ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0,
|
||||
wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE));
|
||||
}
|
||||
|
||||
|
|
@ -1214,16 +1257,19 @@ wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev)
|
|||
CFGP2P_ERR(("wl p2p error %d\n", ret));
|
||||
return 0;
|
||||
}
|
||||
if (p2p_supported)
|
||||
if (p2p_supported == 1) {
|
||||
CFGP2P_INFO(("p2p is supported\n"));
|
||||
|
||||
} else {
|
||||
CFGP2P_INFO(("p2p is unsupported\n"));
|
||||
p2p_supported = 0;
|
||||
}
|
||||
return p2p_supported;
|
||||
}
|
||||
/* Cleanup P2P resources */
|
||||
s32
|
||||
wl_cfgp2p_down(struct wl_priv *wl)
|
||||
{
|
||||
if (wl->p2p.listen_timer)
|
||||
del_timer_sync(wl->p2p.listen_timer);
|
||||
if (wl->p2p->listen_timer)
|
||||
del_timer_sync(wl->p2p->listen_timer);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,16 +91,20 @@ enum wl_cfgp2p_status {
|
|||
};
|
||||
|
||||
|
||||
#define wl_to_p2p_bss_ndev(w, type) ((wl)->p2p.bss_idx[type].dev)
|
||||
#define wl_to_p2p_bss_bssidx(w, type) ((wl)->p2p.bss_idx[type].bssidx)
|
||||
#define wl_to_p2p_bss_saved_ie(w, type) ((wl)->p2p.bss_idx[type].saved_ie)
|
||||
#define wl_to_p2p_bss(wl, type) ((wl)->p2p.bss_idx[type])
|
||||
#define wl_get_p2p_status(wl, stat) (test_bit(WLP2P_STATUS_ ## stat, &(wl)->p2p.status))
|
||||
#define wl_set_p2p_status(wl, stat) (set_bit(WLP2P_STATUS_ ## stat, &(wl)->p2p.status))
|
||||
#define wl_clr_p2p_status(wl, stat) (clear_bit(WLP2P_STATUS_ ## stat, &(wl)->p2p.status))
|
||||
#define wl_chg_p2p_status(wl, stat) (change_bit(WLP2P_STATUS_ ## stat, &(wl)->p2p.status))
|
||||
#define p2p_on(wl) ((wl)->p2p.on)
|
||||
#define p2p_scan(wl) ((wl)->p2p.scan)
|
||||
#define wl_to_p2p_bss_ndev(w, type) ((wl)->p2p->bss_idx[type].dev)
|
||||
#define wl_to_p2p_bss_bssidx(w, type) ((wl)->p2p->bss_idx[type].bssidx)
|
||||
#define wl_to_p2p_bss_saved_ie(w, type) ((wl)->p2p->bss_idx[type].saved_ie)
|
||||
#define wl_to_p2p_bss(wl, type) ((wl)->p2p->bss_idx[type])
|
||||
#define wl_get_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0 : test_bit(WLP2P_STATUS_ ## stat, \
|
||||
&(wl)->p2p->status))
|
||||
#define wl_set_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? : set_bit(WLP2P_STATUS_ ## stat, \
|
||||
&(wl)->p2p->status))
|
||||
#define wl_clr_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? : clear_bit(WLP2P_STATUS_ ## stat, \
|
||||
&(wl)->p2p->status))
|
||||
#define wl_chg_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? : change_bit(WLP2P_STATUS_ ## stat, \
|
||||
&(wl)->p2p->status))
|
||||
#define p2p_on(wl) ((wl)->p2p->on)
|
||||
#define p2p_scan(wl) ((wl)->p2p->scan)
|
||||
|
||||
|
||||
/* dword align allocation */
|
||||
|
|
@ -131,8 +135,10 @@ enum wl_cfgp2p_status {
|
|||
} while (0)
|
||||
|
||||
|
||||
extern void
|
||||
extern s32
|
||||
wl_cfgp2p_init_priv(struct wl_priv *wl);
|
||||
extern void
|
||||
wl_cfgp2p_deinit_priv(struct wl_priv *wl);
|
||||
extern s32
|
||||
wl_cfgp2p_set_firm_p2p(struct wl_priv *wl);
|
||||
extern s32
|
||||
|
|
@ -171,7 +177,7 @@ wl_cfgp2p_find_p2pie(u8 *parse, u32 len);
|
|||
|
||||
extern s32
|
||||
wl_cfgp2p_set_managment_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx,
|
||||
s32 pktflag, const u8 *p2p_ie, u32 p2p_ie_len);
|
||||
s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len);
|
||||
extern s32
|
||||
wl_cfgp2p_clear_management_ie(struct wl_priv *wl, s32 bssidx);
|
||||
|
||||
|
|
@ -220,5 +226,7 @@ wl_cfgp2p_down(struct wl_priv *wl);
|
|||
#define SOCIAL_CHAN_3 11
|
||||
#define WL_P2P_WILDCARD_SSID "DIRECT-"
|
||||
#define WL_P2P_WILDCARD_SSID_LEN 7
|
||||
#define WL_P2P_INTERFACE_PREFIX "p2p"
|
||||
#define WL_P2P_TEMP_CHAN "11"
|
||||
#define IS_P2P_SSID(ssid) (memcmp(ssid, WL_P2P_WILDCARD_SSID, WL_P2P_WILDCARD_SSID_LEN) == 0)
|
||||
#endif /* _wl_cfgp2p_h_ */
|
||||
|
|
|
|||
|
|
@ -1889,6 +1889,12 @@ iwpriv_set_ap_config(struct net_device *dev,
|
|||
info->cmd, info->flags,
|
||||
wrqu->data.pointer, wrqu->data.length));
|
||||
|
||||
if (!ap_fw_loaded) {
|
||||
WL_ERROR(("Can't execute %s(), SOFTAP fw is not Loaded\n",
|
||||
__FUNCTION__));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wrqu->data.length != 0) {
|
||||
|
||||
char *str_ptr;
|
||||
|
|
@ -1955,13 +1961,14 @@ static int iwpriv_get_assoc_list(struct net_device *dev,
|
|||
|
||||
iw = *(wl_iw_t **)netdev_priv(dev);
|
||||
|
||||
MUTEX_LOCK_SOFTAP_SET(iw->pub);
|
||||
DHD_OS_WAKE_LOCK(iw->pub);
|
||||
net_os_wake_lock(dev);
|
||||
DHD_OS_MUTEX_LOCK(&wl_softap_lock);
|
||||
|
||||
WL_TRACE(("\n %s: IWPRIV IOCTL: cmd:%hx, flags:%hx, extra:%p, iwp.len:%d,"
|
||||
"iwp.len:%p, iwp.flags:%x \n", __FUNCTION__, info->cmd, info->flags,
|
||||
extra, p_iwrq->data.length, p_iwrq->data.pointer, p_iwrq->data.flags));
|
||||
|
||||
|
||||
memset(sta_maclist, 0, sizeof(mac_buf));
|
||||
|
||||
sta_maclist->count = 8;
|
||||
|
|
@ -2028,8 +2035,9 @@ static int iwpriv_get_assoc_list(struct net_device *dev,
|
|||
}
|
||||
|
||||
func_exit:
|
||||
DHD_OS_WAKE_UNLOCK(iw->pub);
|
||||
MUTEX_UNLOCK_SOFTAP_SET(iw->pub);
|
||||
|
||||
DHD_OS_MUTEX_UNLOCK(&wl_softap_lock);
|
||||
net_os_wake_unlock(dev);
|
||||
|
||||
WL_SOFTAP(("%s: Exited\n", __FUNCTION__));
|
||||
return ret;
|
||||
|
|
@ -4320,6 +4328,10 @@ wl_iw_iscan_get_scan(
|
|||
|
||||
WL_TRACE(("%s return to WE %d bytes APs=%d\n", __FUNCTION__, dwrq->length, counter));
|
||||
|
||||
|
||||
if (!dwrq->length)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -6266,7 +6278,7 @@ thr_wait_for_2nd_eth_dev(void *data)
|
|||
}
|
||||
DHD_OS_WAKE_LOCK(iw->pub);
|
||||
complete(&tsk_ctl->completed);
|
||||
if (down_timeout(&tsk_ctl->sema, msecs_to_jiffies(5000)) != 0) {
|
||||
if (down_timeout(&tsk_ctl->sema, msecs_to_jiffies(1000)) != 0) {
|
||||
#else
|
||||
if (down_interruptible(&tsk_ctl->sema) != 0) {
|
||||
#endif
|
||||
|
|
@ -6289,7 +6301,7 @@ thr_wait_for_2nd_eth_dev(void *data)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
WL_TRACE(("\n>%s: Thread:'softap ethdev IF:%s is detected !!!'\n\n",
|
||||
WL_SOFTAP(("\n>%s: Thread:'softap ethdev IF:%s is detected!'\n\n",
|
||||
__FUNCTION__, ap_net_dev->name));
|
||||
|
||||
ap_cfg_running = TRUE;
|
||||
|
|
@ -6304,7 +6316,7 @@ thr_wait_for_2nd_eth_dev(void *data)
|
|||
|
||||
DHD_OS_WAKE_UNLOCK(iw->pub);
|
||||
|
||||
WL_TRACE(("\n>%s, thread completed\n", __FUNCTION__));
|
||||
WL_SOFTAP(("\n>%s, thread completed\n", __FUNCTION__));
|
||||
|
||||
complete_and_exit(&tsk_ctl->completed, 0);
|
||||
return ret;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user