PageRenderTime 88ms CodeModel.GetById 21ms app.highlight 49ms RepoModel.GetById 1ms app.codeStats 1ms

/drivers/net/wireless/bcmdhd/dhd_linux.c

https://bitbucket.org/wisechild/galaxy-nexus
C | 5041 lines | 3804 code | 837 blank | 400 comment | 685 complexity | fe4e4f51f9d74a59db8f79ca55e59935 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * Broadcom Dongle Host Driver (DHD), Linux-specific network interface
   3 * Basically selected code segments from usb-cdc.c and usb-rndis.c
   4 *
   5 * Copyright (C) 1999-2011, Broadcom Corporation
   6 * 
   7 *         Unless you and Broadcom execute a separate written software license
   8 * agreement governing use of this software, this software is licensed to you
   9 * under the terms of the GNU General Public License version 2 (the "GPL"),
  10 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
  11 * following added to such license:
  12 * 
  13 *      As a special exception, the copyright holders of this software give you
  14 * permission to link this software with independent modules, and to copy and
  15 * distribute the resulting executable under terms of your choice, provided that
  16 * you also meet, for each linked independent module, the terms and conditions of
  17 * the license of that module.  An independent module is a module which is not
  18 * derived from this software.  The special exception does not apply to any
  19 * modifications of the software.
  20 * 
  21 *      Notwithstanding the above, under no circumstances may you combine this
  22 * software in any way with any other Broadcom software provided under a license
  23 * other than the GPL, without Broadcom's express prior written consent.
  24 *
  25 * $Id: dhd_linux.c 287541 2011-10-03 23:48:17Z $
  26 */
  27
  28#include <typedefs.h>
  29#include <linuxver.h>
  30#include <osl.h>
  31
  32#include <linux/init.h>
  33#include <linux/kernel.h>
  34#include <linux/slab.h>
  35#include <linux/skbuff.h>
  36#include <linux/netdevice.h>
  37#include <linux/inetdevice.h>
  38#include <linux/rtnetlink.h>
  39#include <linux/etherdevice.h>
  40#include <linux/random.h>
  41#include <linux/spinlock.h>
  42#include <linux/ethtool.h>
  43#include <linux/fcntl.h>
  44#include <linux/fs.h>
  45
  46#include <asm/uaccess.h>
  47#include <asm/unaligned.h>
  48
  49#include <epivers.h>
  50#include <bcmutils.h>
  51#include <bcmendian.h>
  52
  53#include <proto/ethernet.h>
  54#include <dngl_stats.h>
  55#include <dhd.h>
  56#include <dhd_bus.h>
  57#include <dhd_proto.h>
  58#include <dhd_dbg.h>
  59#ifdef CONFIG_HAS_WAKELOCK
  60#include <linux/wakelock.h>
  61#endif
  62#ifdef WL_CFG80211
  63#include <wl_cfg80211.h>
  64#endif
  65
  66#include <proto/802.11_bta.h>
  67#include <proto/bt_amp_hci.h>
  68#include <dhd_bta.h>
  69
  70#ifdef WLMEDIA_HTSF
  71#include <linux/time.h>
  72#include <htsf.h>
  73
  74#define HTSF_MINLEN 200    /* min. packet length to timestamp */
  75#define HTSF_BUS_DELAY 150 /* assume a fix propagation in us  */
  76#define TSMAX  1000        /* max no. of timing record kept   */
  77#define NUMBIN 34
  78
  79static uint32 tsidx = 0;
  80static uint32 htsf_seqnum = 0;
  81uint32 tsfsync;
  82struct timeval tsync;
  83static uint32 tsport = 5010;
  84
  85typedef struct histo_ {
  86	uint32 bin[NUMBIN];
  87} histo_t;
  88
  89#if !ISPOWEROF2(DHD_SDALIGN)
  90#error DHD_SDALIGN is not a power of 2!
  91#endif
  92
  93static histo_t vi_d1, vi_d2, vi_d3, vi_d4;
  94#endif /* WLMEDIA_HTSF */
  95
  96#if defined(SOFTAP)
  97extern bool ap_cfg_running;
  98extern bool ap_fw_loaded;
  99#endif
 100
 101/* enable HOSTIP cache update from the host side when an eth0:N is up */
 102#define AOE_IP_ALIAS_SUPPORT 1
 103
 104#ifdef PROP_TXSTATUS
 105#include <wlfc_proto.h>
 106#include <dhd_wlfc.h>
 107#endif
 108
 109#include <wl_android.h>
 110
 111#ifdef ARP_OFFLOAD_SUPPORT
 112static int dhd_device_event(struct notifier_block *this,
 113	unsigned long event,
 114	void *ptr);
 115
 116static struct notifier_block dhd_notifier = {
 117	.notifier_call = dhd_device_event
 118};
 119#endif /* ARP_OFFLOAD_SUPPORT */
 120
 121#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
 122#include <linux/suspend.h>
 123volatile bool dhd_mmc_suspend = FALSE;
 124DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait);
 125#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
 126
 127#if defined(OOB_INTR_ONLY)
 128extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable);
 129#endif /* defined(OOB_INTR_ONLY) */
 130#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
 131MODULE_LICENSE("GPL v2");
 132#endif /* LinuxVer */
 133
 134#include <dhd_bus.h>
 135
 136#ifndef PROP_TXSTATUS
 137#define DBUS_RX_BUFFER_SIZE_DHD(net)	(net->mtu + net->hard_header_len + dhd->pub.hdrlen)
 138#else
 139#define DBUS_RX_BUFFER_SIZE_DHD(net)	(net->mtu + net->hard_header_len + dhd->pub.hdrlen + 128)
 140#endif
 141
 142#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15)
 143const char *
 144print_tainted()
 145{
 146	return "";
 147}
 148#endif	/* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */
 149
 150/* Linux wireless extension support */
 151#if defined(CONFIG_WIRELESS_EXT)
 152#include <wl_iw.h>
 153extern wl_iw_extra_params_t  g_wl_iw_params;
 154#endif /* defined(CONFIG_WIRELESS_EXT) */
 155
 156#if defined(CONFIG_HAS_EARLYSUSPEND)
 157#include <linux/earlysuspend.h>
 158extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
 159extern int dhd_get_dtim_skip(dhd_pub_t *dhd);
 160#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
 161
 162#ifdef PKT_FILTER_SUPPORT
 163extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
 164extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
 165#endif
 166
 167/* Interface control information */
 168typedef struct dhd_if {
 169	struct dhd_info *info;			/* back pointer to dhd_info */
 170	/* OS/stack specifics */
 171	struct net_device *net;
 172	struct net_device_stats stats;
 173	int 			idx;			/* iface idx in dongle */
 174	int 			state;			/* interface state */
 175	uint 			subunit;		/* subunit */
 176	uint8			mac_addr[ETHER_ADDR_LEN];	/* assigned MAC address */
 177	bool			attached;		/* Delayed attachment when unset */
 178	bool			txflowcontrol;	/* Per interface flow control indicator */
 179	char			name[IFNAMSIZ+1]; /* linux interface name */
 180	uint8			bssidx;			/* bsscfg index for the interface */
 181	bool			set_multicast;
 182} dhd_if_t;
 183
 184#ifdef WLMEDIA_HTSF
 185typedef struct {
 186	uint32 low;
 187	uint32 high;
 188} tsf_t;
 189
 190typedef struct {
 191	uint32 last_cycle;
 192	uint32 last_sec;
 193	uint32 last_tsf;
 194	uint32 coef;     /* scaling factor */
 195	uint32 coefdec1; /* first decimal  */
 196	uint32 coefdec2; /* second decimal */
 197} htsf_t;
 198
 199typedef struct {
 200	uint32 t1;
 201	uint32 t2;
 202	uint32 t3;
 203	uint32 t4;
 204} tstamp_t;
 205
 206static tstamp_t ts[TSMAX];
 207static tstamp_t maxdelayts;
 208static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0;
 209
 210#endif  /* WLMEDIA_HTSF */
 211
 212/* Local private structure (extension of pub) */
 213typedef struct dhd_info {
 214#if defined(CONFIG_WIRELESS_EXT)
 215	wl_iw_t		iw;		/* wireless extensions state (must be first) */
 216#endif /* defined(CONFIG_WIRELESS_EXT) */
 217
 218	dhd_pub_t pub;
 219
 220	/* For supporting multiple interfaces */
 221	dhd_if_t *iflist[DHD_MAX_IFS];
 222
 223	struct semaphore proto_sem;
 224#ifdef PROP_TXSTATUS
 225	spinlock_t	wlfc_spinlock;
 226#endif /* PROP_TXSTATUS */
 227#ifdef WLMEDIA_HTSF
 228	htsf_t  htsf;
 229#endif
 230	wait_queue_head_t ioctl_resp_wait;
 231	struct timer_list timer;
 232	bool wd_timer_valid;
 233	struct tasklet_struct tasklet;
 234	spinlock_t	sdlock;
 235	spinlock_t	txqlock;
 236	spinlock_t	dhd_lock;
 237#ifdef DHDTHREAD
 238	/* Thread based operation */
 239	bool threads_only;
 240	struct semaphore sdsem;
 241
 242	tsk_ctl_t	thr_dpc_ctl;
 243	tsk_ctl_t	thr_wdt_ctl;
 244
 245#else
 246	bool dhd_tasklet_create;
 247#endif /* DHDTHREAD */
 248	tsk_ctl_t	thr_sysioc_ctl;
 249
 250	/* Wakelocks */
 251#if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
 252	struct wake_lock wl_wifi;   /* Wifi wakelock */
 253	struct wake_lock wl_rxwake; /* Wifi rx wakelock */
 254#endif
 255
 256#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25))
 257	/* net_device interface lock, prevent race conditions among net_dev interface
 258	 * calls and wifi_on or wifi_off
 259	 */
 260	struct mutex dhd_net_if_mutex;
 261#endif
 262	spinlock_t wakelock_spinlock;
 263	int wakelock_counter;
 264	int wakelock_timeout_enable;
 265
 266	/* Thread to issue ioctl for multicast */
 267	bool set_macaddress;
 268	struct ether_addr macvalue;
 269	wait_queue_head_t ctrl_wait;
 270	atomic_t pend_8021x_cnt;
 271	dhd_attach_states_t dhd_state;
 272
 273#ifdef CONFIG_HAS_EARLYSUSPEND
 274	struct early_suspend early_suspend;
 275#endif /* CONFIG_HAS_EARLYSUSPEND */
 276} dhd_info_t;
 277
 278/* Definitions to provide path to the firmware and nvram
 279 * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt"
 280 */
 281char firmware_path[MOD_PARAM_PATHLEN];
 282char nvram_path[MOD_PARAM_PATHLEN];
 283
 284extern int wl_control_wl_start(struct net_device *dev);
 285extern int net_os_send_hang_message(struct net_device *dev);
 286#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
 287struct semaphore dhd_registration_sem;
 288#define DHD_REGISTRATION_TIMEOUT  12000  /* msec : allowed time to finished dhd registration */
 289#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */
 290
 291/* Spawn a thread for system ioctls (set mac, set mcast) */
 292uint dhd_sysioc = TRUE;
 293module_param(dhd_sysioc, uint, 0);
 294
 295/* Error bits */
 296module_param(dhd_msg_level, int, 0);
 297
 298/* load firmware and/or nvram values from the filesystem */
 299module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660);
 300module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0);
 301
 302/* Watchdog interval */
 303uint dhd_watchdog_ms = 10;
 304module_param(dhd_watchdog_ms, uint, 0);
 305
 306#if defined(DHD_DEBUG)
 307/* Console poll interval */
 308uint dhd_console_ms = 0;
 309module_param(dhd_console_ms, uint, 0);
 310#endif /* defined(DHD_DEBUG) */
 311
 312/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */
 313uint dhd_arp_mode = 0xb;
 314module_param(dhd_arp_mode, uint, 0);
 315
 316/* ARP offload enable */
 317uint dhd_arp_enable = TRUE;
 318module_param(dhd_arp_enable, uint, 0);
 319
 320/* Global Pkt filter enable control */
 321uint dhd_pkt_filter_enable = TRUE;
 322module_param(dhd_pkt_filter_enable, uint, 0);
 323
 324/*  Pkt filter init setup */
 325uint dhd_pkt_filter_init = 0;
 326module_param(dhd_pkt_filter_init, uint, 0);
 327
 328/* Pkt filter mode control */
 329uint dhd_master_mode = TRUE;
 330module_param(dhd_master_mode, uint, 1);
 331
 332#ifdef DHDTHREAD
 333/* Watchdog thread priority, -1 to use kernel timer */
 334int dhd_watchdog_prio = 97;
 335module_param(dhd_watchdog_prio, int, 0);
 336
 337/* DPC thread priority, -1 to use tasklet */
 338int dhd_dpc_prio = 98;
 339module_param(dhd_dpc_prio, int, 0);
 340
 341/* DPC thread priority, -1 to use tasklet */
 342extern int dhd_dongle_memsize;
 343module_param(dhd_dongle_memsize, int, 0);
 344#endif /* DHDTHREAD */
 345/* Control fw roaming */
 346uint dhd_roam_disable = 0;
 347
 348/* Control radio state */
 349uint dhd_radio_up = 1;
 350
 351/* Network inteface name */
 352char iface_name[IFNAMSIZ];
 353module_param_string(iface_name, iface_name, IFNAMSIZ, 0);
 354
 355#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
 356#define DAEMONIZE(a) daemonize(a); \
 357	allow_signal(SIGKILL); \
 358	allow_signal(SIGTERM);
 359#else /* Linux 2.4 (w/o preemption patch) */
 360#define RAISE_RX_SOFTIRQ() \
 361	cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
 362#define DAEMONIZE(a) daemonize(); \
 363	do { if (a) \
 364		strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \
 365	} while (0);
 366#endif /* LINUX_VERSION_CODE  */
 367
 368#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0))
 369#define BLOCKABLE()	(!in_atomic())
 370#else
 371#define BLOCKABLE()	(!in_interrupt())
 372#endif
 373
 374/* The following are specific to the SDIO dongle */
 375
 376/* IOCTL response timeout */
 377int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT;
 378
 379/* Idle timeout for backplane clock */
 380int dhd_idletime = DHD_IDLETIME_TICKS;
 381module_param(dhd_idletime, int, 0);
 382
 383/* Use polling */
 384uint dhd_poll = FALSE;
 385module_param(dhd_poll, uint, 0);
 386
 387/* Use interrupts */
 388uint dhd_intr = TRUE;
 389module_param(dhd_intr, uint, 0);
 390
 391/* SDIO Drive Strength (in milliamps) */
 392uint dhd_sdiod_drive_strength = 6;
 393module_param(dhd_sdiod_drive_strength, uint, 0);
 394
 395/* Tx/Rx bounds */
 396extern uint dhd_txbound;
 397extern uint dhd_rxbound;
 398module_param(dhd_txbound, uint, 0);
 399module_param(dhd_rxbound, uint, 0);
 400
 401/* Deferred transmits */
 402extern uint dhd_deferred_tx;
 403module_param(dhd_deferred_tx, uint, 0);
 404
 405#ifdef BCMDBGFS
 406extern void dhd_dbg_init(dhd_pub_t *dhdp);
 407extern void dhd_dbg_remove(void);
 408#endif /* BCMDBGFS */
 409
 410
 411
 412#ifdef SDTEST
 413/* Echo packet generator (pkts/s) */
 414uint dhd_pktgen = 0;
 415module_param(dhd_pktgen, uint, 0);
 416
 417/* Echo packet len (0 => sawtooth, max 2040) */
 418uint dhd_pktgen_len = 0;
 419module_param(dhd_pktgen_len, uint, 0);
 420#endif /* SDTEST */
 421
 422/* Version string to report */
 423#ifdef DHD_DEBUG
 424#ifndef SRCBASE
 425#define SRCBASE        "drivers/net/wireless/bcmdhd"
 426#endif
 427#define DHD_COMPILED "\nCompiled in " SRCBASE
 428#else
 429#define DHD_COMPILED
 430#endif /* DHD_DEBUG */
 431
 432static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR
 433#ifdef DHD_DEBUG
 434"\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__
 435#endif
 436;
 437static void dhd_net_if_lock_local(dhd_info_t *dhd);
 438static void dhd_net_if_unlock_local(dhd_info_t *dhd);
 439
 440#ifdef WLMEDIA_HTSF
 441void htsf_update(dhd_info_t *dhd, void *data);
 442tsf_t prev_tsf, cur_tsf;
 443
 444uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx);
 445static int dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx);
 446static void dhd_dump_latency(void);
 447static void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf);
 448static void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf);
 449static void dhd_dump_htsfhisto(histo_t *his, char *s);
 450#endif /* WLMEDIA_HTSF */
 451
 452/* Monitor interface */
 453int dhd_monitor_init(void *dhd_pub);
 454int dhd_monitor_uninit(void);
 455
 456
 457#if defined(CONFIG_WIRELESS_EXT)
 458struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
 459#endif /* defined(CONFIG_WIRELESS_EXT) */
 460
 461static void dhd_dpc(ulong data);
 462/* forward decl */
 463extern int dhd_wait_pend8021x(struct net_device *dev);
 464
 465#ifdef TOE
 466#ifndef BDC
 467#error TOE requires BDC
 468#endif /* !BDC */
 469static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol);
 470static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol);
 471#endif /* TOE */
 472
 473static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
 474                             wl_event_msg_t *event_ptr, void **data_ptr);
 475
 476#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
 477static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored)
 478{
 479	int ret = NOTIFY_DONE;
 480
 481	switch (action)	{
 482		case PM_HIBERNATION_PREPARE:
 483		case PM_SUSPEND_PREPARE:
 484			dhd_mmc_suspend = TRUE;
 485		ret = NOTIFY_OK;
 486		break;
 487		case PM_POST_HIBERNATION:
 488		case PM_POST_SUSPEND:
 489			dhd_mmc_suspend = FALSE;
 490		ret = NOTIFY_OK;
 491		break;
 492	}
 493	smp_mb();
 494	return ret;
 495}
 496
 497static struct notifier_block dhd_sleep_pm_notifier = {
 498	.notifier_call = dhd_sleep_pm_callback,
 499	.priority = 0
 500};
 501extern int register_pm_notifier(struct notifier_block *nb);
 502extern int unregister_pm_notifier(struct notifier_block *nb);
 503#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */
 504
 505static void dhd_set_packet_filter(int value, dhd_pub_t *dhd)
 506{
 507#ifdef PKT_FILTER_SUPPORT
 508	DHD_TRACE(("%s: %d\n", __FUNCTION__, value));
 509	/* 1 - Enable packet filter, only allow unicast packet to send up */
 510	/* 0 - Disable packet filter */
 511	if (dhd_pkt_filter_enable) {
 512		int i;
 513
 514		for (i = 0; i < dhd->pktfilter_count; i++) {
 515			dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
 516			dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
 517				value, dhd_master_mode);
 518		}
 519	}
 520#endif
 521}
 522
 523#if defined(CONFIG_HAS_EARLYSUSPEND)
 524static int dhd_set_suspend(int value, dhd_pub_t *dhd)
 525{
 526	int power_mode = PM_FAST;
 527	/* wl_pkt_filter_enable_t	enable_parm; */
 528	char iovbuf[32];
 529	int bcn_li_dtim = 3;
 530	uint roamvar = 1;
 531
 532	DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n",
 533		__FUNCTION__, value, dhd->in_suspend));
 534
 535	if (dhd && dhd->up) {
 536		if (value && dhd->in_suspend) {
 537
 538				/* Kernel suspended */
 539				DHD_ERROR(("%s: force extra Suspend setting \n", __FUNCTION__));
 540
 541				dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode,
 542				                 sizeof(power_mode), TRUE, 0);
 543
 544				/* Enable packet filter, only allow unicast packet to send up */
 545				dhd_set_packet_filter(1, dhd);
 546
 547				/* If DTIM skip is set up as default, force it to wake
 548				 * each third DTIM for better power savings.  Note that
 549				 * one side effect is a chance to miss BC/MC packet.
 550				 */
 551				bcn_li_dtim = dhd_get_dtim_skip(dhd);
 552				bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
 553					4, iovbuf, sizeof(iovbuf));
 554				dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
 555
 556				/* Disable firmware roaming during suspend */
 557				bcm_mkiovar("roam_off", (char *)&roamvar, 4,
 558					iovbuf, sizeof(iovbuf));
 559				dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
 560			} else {
 561
 562				/* Kernel resumed  */
 563				DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__));
 564
 565				power_mode = PM_FAST;
 566				dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode,
 567				                 sizeof(power_mode), TRUE, 0);
 568
 569				/* disable pkt filter */
 570				dhd_set_packet_filter(0, dhd);
 571
 572				/* restore pre-suspend setting for dtim_skip */
 573				bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip,
 574					4, iovbuf, sizeof(iovbuf));
 575
 576				dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
 577				roamvar = dhd_roam_disable;
 578				bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf,
 579					sizeof(iovbuf));
 580				dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
 581			}
 582	}
 583
 584	return 0;
 585}
 586
 587static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val)
 588{
 589	dhd_pub_t *dhdp = &dhd->pub;
 590
 591	DHD_OS_WAKE_LOCK(dhdp);
 592	/* Set flag when early suspend was called */
 593	dhdp->in_suspend = val;
 594	if (!dhdp->suspend_disable_flag)
 595		dhd_set_suspend(val, dhdp);
 596	DHD_OS_WAKE_UNLOCK(dhdp);
 597}
 598
 599static void dhd_early_suspend(struct early_suspend *h)
 600{
 601	struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
 602
 603	DHD_TRACE(("%s: enter\n", __FUNCTION__));
 604
 605	if (dhd)
 606		dhd_suspend_resume_helper(dhd, 1);
 607}
 608
 609static void dhd_late_resume(struct early_suspend *h)
 610{
 611	struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend);
 612
 613	DHD_TRACE(("%s: enter\n", __FUNCTION__));
 614
 615	if (dhd)
 616		dhd_suspend_resume_helper(dhd, 0);
 617}
 618#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
 619
 620/*
 621 * Generalized timeout mechanism.  Uses spin sleep with exponential back-off until
 622 * the sleep time reaches one jiffy, then switches over to task delay.  Usage:
 623 *
 624 *      dhd_timeout_start(&tmo, usec);
 625 *      while (!dhd_timeout_expired(&tmo))
 626 *              if (poll_something())
 627 *                      break;
 628 *      if (dhd_timeout_expired(&tmo))
 629 *              fatal();
 630 */
 631
 632void
 633dhd_timeout_start(dhd_timeout_t *tmo, uint usec)
 634{
 635	tmo->limit = usec;
 636	tmo->increment = 0;
 637	tmo->elapsed = 0;
 638	tmo->tick = 1000000 / HZ;
 639}
 640
 641int
 642dhd_timeout_expired(dhd_timeout_t *tmo)
 643{
 644	/* Does nothing the first call */
 645	if (tmo->increment == 0) {
 646		tmo->increment = 1;
 647		return 0;
 648	}
 649
 650	if (tmo->elapsed >= tmo->limit)
 651		return 1;
 652
 653	/* Add the delay that's about to take place */
 654	tmo->elapsed += tmo->increment;
 655
 656	if (tmo->increment < tmo->tick) {
 657		OSL_DELAY(tmo->increment);
 658		tmo->increment *= 2;
 659		if (tmo->increment > tmo->tick)
 660			tmo->increment = tmo->tick;
 661	} else {
 662		wait_queue_head_t delay_wait;
 663		DECLARE_WAITQUEUE(wait, current);
 664		int pending;
 665		init_waitqueue_head(&delay_wait);
 666		add_wait_queue(&delay_wait, &wait);
 667		set_current_state(TASK_INTERRUPTIBLE);
 668		schedule_timeout(1);
 669		pending = signal_pending(current);
 670		remove_wait_queue(&delay_wait, &wait);
 671		set_current_state(TASK_RUNNING);
 672		if (pending)
 673			return 1;	/* Interrupted */
 674	}
 675
 676	return 0;
 677}
 678
 679int
 680dhd_net2idx(dhd_info_t *dhd, struct net_device *net)
 681{
 682	int i = 0;
 683
 684	ASSERT(dhd);
 685	while (i < DHD_MAX_IFS) {
 686		if (dhd->iflist[i] && (dhd->iflist[i]->net == net))
 687			return i;
 688		i++;
 689	}
 690
 691	return DHD_BAD_IF;
 692}
 693
 694struct net_device * dhd_idx2net(struct dhd_pub *dhd_pub, int ifidx)
 695{
 696	struct dhd_info *dhd_info;
 697
 698	if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS)
 699		return NULL;
 700	dhd_info = dhd_pub->info;
 701	if (dhd_info && dhd_info->iflist[ifidx])
 702		return dhd_info->iflist[ifidx]->net;
 703	return NULL;
 704}
 705
 706int
 707dhd_ifname2idx(dhd_info_t *dhd, char *name)
 708{
 709	int i = DHD_MAX_IFS;
 710
 711	ASSERT(dhd);
 712
 713	if (name == NULL || *name == '\0')
 714		return 0;
 715
 716	while (--i > 0)
 717		if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ))
 718				break;
 719
 720	DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name));
 721
 722	return i;	/* default - the primary interface */
 723}
 724
 725char *
 726dhd_ifname(dhd_pub_t *dhdp, int ifidx)
 727{
 728	dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
 729
 730	ASSERT(dhd);
 731
 732	if (ifidx < 0 || ifidx >= DHD_MAX_IFS) {
 733		DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx));
 734		return "<if_bad>";
 735	}
 736
 737	if (dhd->iflist[ifidx] == NULL) {
 738		DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx));
 739		return "<if_null>";
 740	}
 741
 742	if (dhd->iflist[ifidx]->net)
 743		return dhd->iflist[ifidx]->net->name;
 744
 745	return "<if_none>";
 746}
 747
 748uint8 *
 749dhd_bssidx2bssid(dhd_pub_t *dhdp, int idx)
 750{
 751	int i;
 752	dhd_info_t *dhd = (dhd_info_t *)dhdp;
 753
 754	ASSERT(dhd);
 755	for (i = 0; i < DHD_MAX_IFS; i++)
 756	if (dhd->iflist[i] && dhd->iflist[i]->bssidx == idx)
 757		return dhd->iflist[i]->mac_addr;
 758
 759	return NULL;
 760}
 761
 762
 763static void
 764_dhd_set_multicast_list(dhd_info_t *dhd, int ifidx)
 765{
 766	struct net_device *dev;
 767#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
 768	struct netdev_hw_addr *ha;
 769#else
 770	struct dev_mc_list *mclist;
 771#endif
 772	uint32 allmulti, cnt;
 773
 774	wl_ioctl_t ioc;
 775	char *buf, *bufp;
 776	uint buflen;
 777	int ret;
 778
 779	ASSERT(dhd && dhd->iflist[ifidx]);
 780	dev = dhd->iflist[ifidx]->net;
 781#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
 782	netif_addr_lock_bh(dev);
 783#endif
 784#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
 785	cnt = netdev_mc_count(dev);
 786#else
 787	cnt = dev->mc_count;
 788#endif
 789#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
 790	netif_addr_unlock_bh(dev);
 791#endif
 792
 793	/* Determine initial value of allmulti flag */
 794	allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE;
 795
 796	/* Send down the multicast list first. */
 797
 798
 799	buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN);
 800	if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) {
 801		DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n",
 802		           dhd_ifname(&dhd->pub, ifidx), cnt));
 803		return;
 804	}
 805
 806	strcpy(bufp, "mcast_list");
 807	bufp += strlen("mcast_list") + 1;
 808
 809	cnt = htol32(cnt);
 810	memcpy(bufp, &cnt, sizeof(cnt));
 811	bufp += sizeof(cnt);
 812
 813#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
 814	netif_addr_lock_bh(dev);
 815#endif
 816#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
 817	netdev_for_each_mc_addr(ha, dev) {
 818		if (!cnt)
 819			break;
 820		memcpy(bufp, ha->addr, ETHER_ADDR_LEN);
 821		bufp += ETHER_ADDR_LEN;
 822		cnt--;
 823	}
 824#else
 825	for (mclist = dev->mc_list; (mclist && (cnt > 0)); cnt--, mclist = mclist->next) {
 826		memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN);
 827		bufp += ETHER_ADDR_LEN;
 828	}
 829#endif
 830#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
 831	netif_addr_unlock_bh(dev);
 832#endif
 833
 834	memset(&ioc, 0, sizeof(ioc));
 835	ioc.cmd = WLC_SET_VAR;
 836	ioc.buf = buf;
 837	ioc.len = buflen;
 838	ioc.set = TRUE;
 839
 840	ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
 841	if (ret < 0) {
 842		DHD_ERROR(("%s: set mcast_list failed, cnt %d\n",
 843			dhd_ifname(&dhd->pub, ifidx), cnt));
 844		allmulti = cnt ? TRUE : allmulti;
 845	}
 846
 847	MFREE(dhd->pub.osh, buf, buflen);
 848
 849	/* Now send the allmulti setting.  This is based on the setting in the
 850	 * net_device flags, but might be modified above to be turned on if we
 851	 * were trying to set some addresses and dongle rejected it...
 852	 */
 853
 854	buflen = sizeof("allmulti") + sizeof(allmulti);
 855	if (!(buf = MALLOC(dhd->pub.osh, buflen))) {
 856		DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx)));
 857		return;
 858	}
 859	allmulti = htol32(allmulti);
 860
 861	if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) {
 862		DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n",
 863		           dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen));
 864		MFREE(dhd->pub.osh, buf, buflen);
 865		return;
 866	}
 867
 868
 869	memset(&ioc, 0, sizeof(ioc));
 870	ioc.cmd = WLC_SET_VAR;
 871	ioc.buf = buf;
 872	ioc.len = buflen;
 873	ioc.set = TRUE;
 874
 875	ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
 876	if (ret < 0) {
 877		DHD_ERROR(("%s: set allmulti %d failed\n",
 878		           dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
 879	}
 880
 881	MFREE(dhd->pub.osh, buf, buflen);
 882
 883	/* Finally, pick up the PROMISC flag as well, like the NIC driver does */
 884
 885	allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE;
 886	allmulti = htol32(allmulti);
 887
 888	memset(&ioc, 0, sizeof(ioc));
 889	ioc.cmd = WLC_SET_PROMISC;
 890	ioc.buf = &allmulti;
 891	ioc.len = sizeof(allmulti);
 892	ioc.set = TRUE;
 893
 894	ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
 895	if (ret < 0) {
 896		DHD_ERROR(("%s: set promisc %d failed\n",
 897		           dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti)));
 898	}
 899}
 900
 901static int
 902_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr)
 903{
 904	char buf[32];
 905	wl_ioctl_t ioc;
 906	int ret;
 907
 908	if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) {
 909		DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx)));
 910		return -1;
 911	}
 912	memset(&ioc, 0, sizeof(ioc));
 913	ioc.cmd = WLC_SET_VAR;
 914	ioc.buf = buf;
 915	ioc.len = 32;
 916	ioc.set = TRUE;
 917
 918	ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len);
 919	if (ret < 0) {
 920		DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx)));
 921	} else {
 922		memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN);
 923	}
 924
 925	return ret;
 926}
 927
 928#ifdef SOFTAP
 929extern struct net_device *ap_net_dev;
 930extern tsk_ctl_t ap_eth_ctl; /* ap netdev heper thread ctl */
 931#endif
 932
 933static void
 934dhd_op_if(dhd_if_t *ifp)
 935{
 936	dhd_info_t *dhd;
 937	int ret = 0, err = 0;
 938#ifdef SOFTAP
 939	unsigned long flags;
 940#endif
 941
 942	ASSERT(ifp && ifp->info && ifp->idx);	/* Virtual interfaces only */
 943
 944	dhd = ifp->info;
 945
 946	DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state));
 947
 948#ifdef WL_CFG80211
 949	if (wl_cfg80211_is_progress_ifchange())
 950			return;
 951
 952#endif
 953	switch (ifp->state) {
 954	case WLC_E_IF_ADD:
 955		/*
 956		 * Delete the existing interface before overwriting it
 957		 * in case we missed the WLC_E_IF_DEL event.
 958		 */
 959		if (ifp->net != NULL) {
 960			DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n",
 961			 __FUNCTION__, ifp->net->name));
 962			netif_stop_queue(ifp->net);
 963			unregister_netdev(ifp->net);
 964			free_netdev(ifp->net);
 965		}
 966		/* Allocate etherdev, including space for private structure */
 967		if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) {
 968			DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
 969			ret = -ENOMEM;
 970		}
 971		if (ret == 0) {
 972			strncpy(ifp->net->name, ifp->name, IFNAMSIZ);
 973			ifp->net->name[IFNAMSIZ - 1] = '\0';
 974			memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd));
 975#ifdef WL_CFG80211
 976			if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)
 977				if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx,
 978					dhd_net_attach)) {
 979					ifp->state = 0;
 980					return;
 981				}
 982#endif
 983			if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) {
 984				DHD_ERROR(("%s: dhd_net_attach failed, err %d\n",
 985					__FUNCTION__, err));
 986				ret = -EOPNOTSUPP;
 987			} else {
 988#if defined(SOFTAP)
 989		if (ap_fw_loaded && !(dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) {
 990				 /* semaphore that the soft AP CODE waits on */
 991				flags = dhd_os_spin_lock(&dhd->pub);
 992
 993				/* save ptr to wl0.1 netdev for use in wl_iw.c  */
 994				ap_net_dev = ifp->net;
 995				 /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */
 996				up(&ap_eth_ctl.sema);
 997				dhd_os_spin_unlock(&dhd->pub, flags);
 998		}
 999#endif
1000				DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n",
1001					current->pid, ifp->net->name));
1002				ifp->state = 0;
1003			}
1004		}
1005		break;
1006	case WLC_E_IF_DEL:
1007		if (ifp->net != NULL) {
1008			DHD_TRACE(("\n%s: got 'WLC_E_IF_DEL' state\n", __FUNCTION__));
1009#ifdef WL_CFG80211
1010			wl_cfg80211_ifdel_ops(ifp->net);
1011#endif
1012			netif_stop_queue(ifp->net);
1013			unregister_netdev(ifp->net);
1014			ret = DHD_DEL_IF;	/* Make sure the free_netdev() is called */
1015		}
1016		break;
1017	default:
1018		DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state));
1019		ASSERT(!ifp->state);
1020		break;
1021	}
1022
1023	if (ret < 0) {
1024		ifp->set_multicast = FALSE;
1025		if (ifp->net) {
1026			free_netdev(ifp->net);
1027		}
1028		dhd->iflist[ifp->idx] = NULL;
1029#ifdef WL_CFG80211
1030		if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) {
1031			wl_cfg80211_notify_ifdel(ifp->net);
1032		}
1033#endif
1034#ifdef SOFTAP
1035		flags = dhd_os_spin_lock(&dhd->pub);
1036		if (ifp->net == ap_net_dev)
1037			ap_net_dev = NULL;   /*  NULL  SOFTAP global wl0.1 as well */
1038		dhd_os_spin_unlock(&dhd->pub, flags);
1039#endif /*  SOFTAP */
1040		MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
1041	}
1042}
1043
1044static int
1045_dhd_sysioc_thread(void *data)
1046{
1047	tsk_ctl_t *tsk = (tsk_ctl_t *)data;
1048	dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
1049
1050
1051	int i;
1052#ifdef SOFTAP
1053	bool in_ap = FALSE;
1054	unsigned long flags;
1055#endif
1056
1057	DAEMONIZE("dhd_sysioc");
1058
1059	complete(&tsk->completed);
1060
1061	while (down_interruptible(&tsk->sema) == 0) {
1062
1063		SMP_RD_BARRIER_DEPENDS();
1064		if (tsk->terminated) {
1065			break;
1066		}
1067
1068		dhd_net_if_lock_local(dhd);
1069		DHD_OS_WAKE_LOCK(&dhd->pub);
1070
1071		for (i = 0; i < DHD_MAX_IFS; i++) {
1072			if (dhd->iflist[i]) {
1073				DHD_TRACE(("%s: interface %d\n", __FUNCTION__, i));
1074#ifdef SOFTAP
1075				flags = dhd_os_spin_lock(&dhd->pub);
1076				in_ap = (ap_net_dev != NULL);
1077				dhd_os_spin_unlock(&dhd->pub, flags);
1078#endif /* SOFTAP */
1079
1080				if (dhd->iflist[i] && dhd->iflist[i]->state)
1081					dhd_op_if(dhd->iflist[i]);
1082
1083				if (dhd->iflist[i] == NULL) {
1084					DHD_TRACE(("\n\n %s: interface %d just been removed,"
1085						"!\n\n", __FUNCTION__, i));
1086					continue;
1087				}
1088
1089#ifdef SOFTAP
1090				if (in_ap && dhd->set_macaddress)  {
1091					DHD_TRACE(("attempt to set MAC for %s in AP Mode,"
1092						"blocked. \n", dhd->iflist[i]->net->name));
1093					dhd->set_macaddress = FALSE;
1094					continue;
1095				}
1096
1097				if (in_ap && dhd->iflist[i]->set_multicast)  {
1098					DHD_TRACE(("attempt to set MULTICAST list for %s"
1099					 "in AP Mode, blocked. \n", dhd->iflist[i]->net->name));
1100					dhd->iflist[i]->set_multicast = FALSE;
1101					continue;
1102				}
1103#endif /* SOFTAP */
1104				if (dhd->iflist[i]->set_multicast) {
1105					dhd->iflist[i]->set_multicast = FALSE;
1106					_dhd_set_multicast_list(dhd, i);
1107				}
1108				if (dhd->set_macaddress) {
1109					dhd->set_macaddress = FALSE;
1110					_dhd_set_mac_address(dhd, i, &dhd->macvalue);
1111				}
1112			}
1113		}
1114
1115		DHD_OS_WAKE_UNLOCK(&dhd->pub);
1116		dhd_net_if_unlock_local(dhd);
1117	}
1118	DHD_TRACE(("%s: stopped\n", __FUNCTION__));
1119	complete_and_exit(&tsk->completed, 0);
1120}
1121
1122static int
1123dhd_set_mac_address(struct net_device *dev, void *addr)
1124{
1125	int ret = 0;
1126
1127	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
1128	struct sockaddr *sa = (struct sockaddr *)addr;
1129	int ifidx;
1130
1131	ifidx = dhd_net2idx(dhd, dev);
1132	if (ifidx == DHD_BAD_IF)
1133		return -1;
1134
1135	ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0);
1136	memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN);
1137	dhd->set_macaddress = TRUE;
1138	up(&dhd->thr_sysioc_ctl.sema);
1139
1140	return ret;
1141}
1142
1143static void
1144dhd_set_multicast_list(struct net_device *dev)
1145{
1146	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
1147	int ifidx;
1148
1149	ifidx = dhd_net2idx(dhd, dev);
1150	if (ifidx == DHD_BAD_IF)
1151		return;
1152
1153	ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0);
1154	dhd->iflist[ifidx]->set_multicast = TRUE;
1155	up(&dhd->thr_sysioc_ctl.sema);
1156}
1157
1158#ifdef PROP_TXSTATUS
1159int
1160dhd_os_wlfc_block(dhd_pub_t *pub)
1161{
1162	dhd_info_t *di = (dhd_info_t *)(pub->info);
1163	ASSERT(di != NULL);
1164	spin_lock_bh(&di->wlfc_spinlock);
1165	return 1;
1166}
1167
1168int
1169dhd_os_wlfc_unblock(dhd_pub_t *pub)
1170{
1171	dhd_info_t *di = (dhd_info_t *)(pub->info);
1172	ASSERT(di != NULL);
1173	spin_unlock_bh(&di->wlfc_spinlock);
1174	return 1;
1175}
1176
1177const uint8 wme_fifo2ac[] = { 0, 1, 2, 3, 1, 1 };
1178uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
1179#define WME_PRIO2AC(prio)	wme_fifo2ac[prio2fifo[(prio)]]
1180
1181#endif /* PROP_TXSTATUS */
1182int
1183dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf)
1184{
1185	int ret;
1186	dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
1187	struct ether_header *eh = NULL;
1188
1189	/* Reject if down */
1190	if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
1191		/* free the packet here since the caller won't */
1192		PKTFREE(dhdp->osh, pktbuf, TRUE);
1193		return -ENODEV;
1194	}
1195
1196	/* Update multicast statistic */
1197	if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_ADDR_LEN) {
1198		uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf);
1199		eh = (struct ether_header *)pktdata;
1200
1201		if (ETHER_ISMULTI(eh->ether_dhost))
1202			dhdp->tx_multicast++;
1203		if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X)
1204			atomic_inc(&dhd->pend_8021x_cnt);
1205	}
1206
1207	/* Look into the packet and update the packet priority */
1208	if (PKTPRIO(pktbuf) == 0)
1209		pktsetprio(pktbuf, FALSE);
1210
1211#ifdef PROP_TXSTATUS
1212	if (dhdp->wlfc_state) {
1213		/* store the interface ID */
1214		DHD_PKTTAG_SETIF(PKTTAG(pktbuf), ifidx);
1215
1216		/* store destination MAC in the tag as well */
1217		DHD_PKTTAG_SETDSTN(PKTTAG(pktbuf), eh->ether_dhost);
1218
1219		/* decide which FIFO this packet belongs to */
1220		if (ETHER_ISMULTI(eh->ether_dhost))
1221			/* one additional queue index (highest AC + 1) is used for bc/mc queue */
1222			DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), AC_COUNT);
1223		else
1224			DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), WME_PRIO2AC(PKTPRIO(pktbuf)));
1225	} else
1226#endif /* PROP_TXSTATUS */
1227	/* If the protocol uses a data header, apply it */
1228	dhd_prot_hdrpush(dhdp, ifidx, pktbuf);
1229
1230	/* Use bus module to send data frame */
1231#ifdef WLMEDIA_HTSF
1232	dhd_htsf_addtxts(dhdp, pktbuf);
1233#endif
1234#ifdef PROP_TXSTATUS
1235	if (dhdp->wlfc_state && ((athost_wl_status_info_t*)dhdp->wlfc_state)->proptxstatus_mode
1236			!= WLFC_FCMODE_NONE) {
1237		dhd_os_wlfc_block(dhdp);
1238		ret = dhd_wlfc_enque_sendq(dhdp->wlfc_state, DHD_PKTTAG_FIFO(PKTTAG(pktbuf)),
1239			pktbuf);
1240		dhd_wlfc_commit_packets(dhdp->wlfc_state,  (f_commitpkt_t)dhd_bus_txdata,
1241			dhdp->bus);
1242		if (((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if) {
1243			((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if = 0;
1244		}
1245		dhd_os_wlfc_unblock(dhdp);
1246	}
1247	else
1248		/* non-proptxstatus way */
1249	ret = dhd_bus_txdata(dhdp->bus, pktbuf);
1250#else
1251	ret = dhd_bus_txdata(dhdp->bus, pktbuf);
1252#endif /* PROP_TXSTATUS */
1253
1254
1255	return ret;
1256}
1257
1258int
1259dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
1260{
1261	int ret;
1262	void *pktbuf;
1263	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1264	int ifidx;
1265#ifdef WLMEDIA_HTSF
1266	uint8 htsfdlystat_sz = dhd->pub.htsfdlystat_sz;
1267#else
1268	uint8 htsfdlystat_sz = 0;
1269#endif
1270
1271	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1272
1273	DHD_OS_WAKE_LOCK(&dhd->pub);
1274
1275	/* Reject if down */
1276	if (!dhd->pub.up || (dhd->pub.busstate == DHD_BUS_DOWN)) {
1277		DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n",
1278			__FUNCTION__, dhd->pub.up, dhd->pub.busstate));
1279		netif_stop_queue(net);
1280		/* Send Event when bus down detected during data session */
1281		if (dhd->pub.busstate == DHD_BUS_DOWN)  {
1282			DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__));
1283			net_os_send_hang_message(net);
1284		}
1285		DHD_OS_WAKE_UNLOCK(&dhd->pub);
1286		return -ENODEV;
1287	}
1288
1289	ifidx = dhd_net2idx(dhd, net);
1290	if (ifidx == DHD_BAD_IF) {
1291		DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx));
1292		netif_stop_queue(net);
1293		DHD_OS_WAKE_UNLOCK(&dhd->pub);
1294		return -ENODEV;
1295	}
1296
1297	/* Make sure there's enough room for any header */
1298
1299	if (skb_headroom(skb) < dhd->pub.hdrlen + htsfdlystat_sz) {
1300		struct sk_buff *skb2;
1301
1302		DHD_INFO(("%s: insufficient headroom\n",
1303		          dhd_ifname(&dhd->pub, ifidx)));
1304		dhd->pub.tx_realloc++;
1305
1306		skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen + htsfdlystat_sz);
1307
1308		dev_kfree_skb(skb);
1309		if ((skb = skb2) == NULL) {
1310			DHD_ERROR(("%s: skb_realloc_headroom failed\n",
1311			           dhd_ifname(&dhd->pub, ifidx)));
1312			ret = -ENOMEM;
1313			goto done;
1314		}
1315	}
1316
1317	/* Convert to packet */
1318	if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) {
1319		DHD_ERROR(("%s: PKTFRMNATIVE failed\n",
1320		           dhd_ifname(&dhd->pub, ifidx)));
1321		dev_kfree_skb_any(skb);
1322		ret = -ENOMEM;
1323		goto done;
1324	}
1325#ifdef WLMEDIA_HTSF
1326	if (htsfdlystat_sz && PKTLEN(dhd->pub.osh, pktbuf) >= ETHER_ADDR_LEN) {
1327		uint8 *pktdata = (uint8 *)PKTDATA(dhd->pub.osh, pktbuf);
1328		struct ether_header *eh = (struct ether_header *)pktdata;
1329
1330		if (!ETHER_ISMULTI(eh->ether_dhost) &&
1331			(ntoh16(eh->ether_type) == ETHER_TYPE_IP)) {
1332			eh->ether_type = hton16(ETHER_TYPE_BRCM_PKTDLYSTATS);
1333		}
1334	}
1335#endif
1336
1337	ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf);
1338
1339
1340done:
1341	if (ret)
1342		dhd->pub.dstats.tx_dropped++;
1343	else
1344		dhd->pub.tx_packets++;
1345
1346	DHD_OS_WAKE_UNLOCK(&dhd->pub);
1347
1348	/* Return ok: we always eat the packet */
1349	return 0;
1350}
1351
1352void
1353dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state)
1354{
1355	struct net_device *net;
1356	dhd_info_t *dhd = dhdp->info;
1357	int i;
1358
1359	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1360
1361	dhdp->txoff = state;
1362	ASSERT(dhd);
1363
1364	if (ifidx == ALL_INTERFACES) {
1365		/* Flow control on all active interfaces */
1366		for (i = 0; i < DHD_MAX_IFS; i++) {
1367			if (dhd->iflist[i]) {
1368				net = dhd->iflist[i]->net;
1369				if (state == ON)
1370					netif_stop_queue(net);
1371				else
1372					netif_wake_queue(net);
1373			}
1374		}
1375	}
1376	else {
1377		if (dhd->iflist[ifidx]) {
1378			net = dhd->iflist[ifidx]->net;
1379			if (state == ON)
1380				netif_stop_queue(net);
1381			else
1382				netif_wake_queue(net);
1383		}
1384	}
1385}
1386
1387void
1388dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
1389{
1390	dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
1391	struct sk_buff *skb;
1392	uchar *eth;
1393	uint len;
1394	void *data, *pnext = NULL, *save_pktbuf;
1395	int i;
1396	dhd_if_t *ifp;
1397	wl_event_msg_t event;
1398	int tout = DHD_PACKET_TIMEOUT;
1399
1400	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1401
1402	save_pktbuf = pktbuf;
1403
1404	for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) {
1405		struct ether_header *eh;
1406		struct dot11_llc_snap_header *lsh;
1407
1408		ifp = dhd->iflist[ifidx];
1409		if (ifp == NULL) {
1410			DHD_ERROR(("%s: ifp is NULL. drop packet\n",
1411				__FUNCTION__));
1412			PKTFREE(dhdp->osh, pktbuf, TRUE);
1413			continue;
1414		}
1415
1416		/* Dropping packets before registering net device to avoid kernel panic */
1417		if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED ||
1418			!dhd->pub.up) {
1419			DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n",
1420			__FUNCTION__));
1421			PKTFREE(dhdp->osh, pktbuf, TRUE);
1422			continue;
1423		}
1424
1425		pnext = PKTNEXT(dhdp->osh, pktbuf);
1426		PKTSETNEXT(wl->sh.osh, pktbuf, NULL);
1427
1428		eh = (struct ether_header *)PKTDATA(wl->sh.osh, pktbuf);
1429		lsh = (struct dot11_llc_snap_header *)&eh[1];
1430
1431		if ((ntoh16(eh->ether_type) < ETHER_TYPE_MIN) &&
1432		    (PKTLEN(wl->sh.osh, pktbuf) >= RFC1042_HDR_LEN) &&
1433		    bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 &&
1434		    lsh->type == HTON16(BTA_PROT_L2CAP)) {
1435			amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *)
1436			        ((uint8 *)eh + RFC1042_HDR_LEN);
1437			ACL_data = NULL;
1438		}
1439
1440#ifdef PROP_TXSTATUS
1441		if (dhdp->wlfc_state && PKTLEN(wl->sh.osh, pktbuf) == 0) {
1442			/* WLFC may send header only packet when
1443			there is an urgent message but no packet to
1444			piggy-back on
1445			*/
1446			((athost_wl_status_info_t*)dhdp->wlfc_state)->stats.wlfc_header_only_pkt++;
1447			PKTFREE(dhdp->osh, pktbuf, TRUE);
1448			continue;
1449		}
1450#endif
1451
1452		skb = PKTTONATIVE(dhdp->osh, pktbuf);
1453
1454		/* Get the protocol, maintain skb around eth_type_trans()
1455		 * The main reason for this hack is for the limitation of
1456		 * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len'
1457		 * to perform skb_pull inside vs ETH_HLEN. Since to avoid
1458		 * coping of the packet coming from the network stack to add
1459		 * BDC, Hardware header etc, during network interface registration
1460		 * we set the 'net->hard_header_len' to ETH_HLEN + extra space required
1461		 * for BDC, Hardware header etc. and not just the ETH_HLEN
1462		 */
1463		eth = skb->data;
1464		len = skb->len;
1465
1466		ifp = dhd->iflist[ifidx];
1467		if (ifp == NULL)
1468			ifp = dhd->iflist[0];
1469
1470		ASSERT(ifp);
1471		skb->dev = ifp->net;
1472		skb->protocol = eth_type_trans(skb, skb->dev);
1473
1474		if (skb->pkt_type == PACKET_MULTICAST) {
1475			dhd->pub.rx_multicast++;
1476		}
1477
1478		skb->data = eth;
1479		skb->len = len;
1480
1481#ifdef WLMEDIA_HTSF
1482	dhd_htsf_addrxts(dhdp, pktbuf);
1483#endif
1484		/* Strip header, count, deliver upward */
1485		skb_pull(skb, ETH_HLEN);
1486
1487		/* Process special event packets and then discard them */
1488		if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) {
1489			dhd_wl_host_event(dhd, &ifidx,
1490#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
1491			skb->mac_header,
1492#else
1493			skb->mac.raw,
1494#endif
1495			&event,
1496			&data);
1497
1498			wl_event_to_host_order(&event);
1499			if (event.event_type == WLC_E_BTA_HCI_EVENT) {
1500				dhd_bta_doevt(dhdp, data, event.datalen);
1501			}
1502			tout = DHD_EVENT_TIMEOUT;
1503		}
1504
1505		ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]);
1506		if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state)
1507			ifp = dhd->iflist[ifidx];
1508
1509		if (ifp->net)
1510			ifp->net->last_rx = jiffies;
1511
1512		dhdp->dstats.rx_bytes += skb->len;
1513		dhdp->rx_packets++; /* Local count */
1514
1515		if (in_interrupt()) {
1516			netif_rx(skb);
1517		} else {
1518			/* If the receive is not processed inside an ISR,
1519			 * the softirqd must be woken explicitly to service
1520			 * the NET_RX_SOFTIRQ.  In 2.6 kernels, this is handled
1521			 * by netif_rx_ni(), but in earlier kernels, we need
1522			 * to do it manually.
1523			 */
1524#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
1525			netif_rx_ni(skb);
1526#else
1527			ulong flags;
1528			netif_rx(skb);
1529			local_irq_save(flags);
1530			RAISE_RX_SOFTIRQ();
1531			local_irq_restore(flags);
1532#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */
1533		}
1534	}
1535	DHD_OS_WAKE_LOCK_TIMEOUT_ENABLE(dhdp, tout);
1536}
1537
1538void
1539dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx)
1540{
1541	/* Linux version has nothing to do */
1542	return;
1543}
1544
1545void
1546dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success)
1547{
1548	uint ifidx;
1549	dhd_info_t *dhd = (dhd_info_t *)(dhdp->info);
1550	struct ether_header *eh;
1551	uint16 type;
1552	uint len;
1553
1554	dhd_prot_hdrpull(dhdp, &ifidx, txp);
1555
1556	eh = (struct ether_header *)PKTDATA(dhdp->osh, txp);
1557	type  = ntoh16(eh->ether_type);
1558
1559	if (type == ETHER_TYPE_802_1X)
1560		atomic_dec(&dhd->pend_8021x_cnt);
1561
1562	/* Crack open the packet and check to see if it is BT HCI ACL data packet.
1563	 * If yes generate packet completion event.
1564	 */
1565	len = PKTLEN(dhdp->osh, txp);
1566
1567	/* Generate ACL data tx completion event locally to avoid SDIO bus transaction */
1568	if ((type < ETHER_TYPE_MIN) && (len >= RFC1042_HDR_LEN)) {
1569		struct dot11_llc_snap_header *lsh = (struct dot11_llc_snap_header *)&eh[1];
1570
1571		if (bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 &&
1572		    ntoh16(lsh->type) == BTA_PROT_L2CAP) {
1573
1574			dhd_bta_tx_hcidata_complete(dhdp, txp, success);
1575		}
1576	}
1577}
1578
1579static struct net_device_stats *
1580dhd_get_stats(struct net_device *net)
1581{
1582	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1583	dhd_if_t *ifp;
1584	int ifidx;
1585
1586	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1587
1588	ifidx = dhd_net2idx(dhd, net);
1589	if (ifidx == DHD_BAD_IF)
1590		return NULL;
1591
1592	ifp = dhd->iflist[ifidx];
1593	ASSERT(dhd && ifp);
1594
1595	if (dhd->pub.up) {
1596		/* Use the protocol to get dongle stats */
1597		dhd_prot_dstats(&dhd->pub);
1598	}
1599
1600	/* Copy dongle stats to net device stats */
1601	ifp->stats.rx_packets = dhd->pub.dstats.rx_packets;
1602	ifp->stats.tx_packets = dhd->pub.dstats.tx_packets;
1603	ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes;
1604	ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes;
1605	ifp->stats.rx_errors = dhd->pub.dstats.rx_errors;
1606	ifp->stats.tx_errors = dhd->pub.dstats.tx_errors;
1607	ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped;
1608	ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped;
1609	ifp->stats.multicast = dhd->pub.dstats.multicast;
1610
1611	return &ifp->stats;
1612}
1613
1614#ifdef DHDTHREAD
1615static int
1616dhd_watchdog_thread(void *data)
1617{
1618	tsk_ctl_t *tsk = (tsk_ctl_t *)data;
1619	dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
1620	/* This thread doesn't need any user-level access,
1621	 * so get rid of all our resources
1622	 */
1623	if (dhd_watchdog_prio > 0) {
1624		struct sched_param param;
1625		param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)?
1626			dhd_watchdog_prio:(MAX_RT_PRIO-1);
1627		setScheduler(current, SCHED_FIFO, &param);
1628	}
1629
1630	DAEMONIZE("dhd_watchdog");
1631
1632	/* Run until signal received */
1633	complete(&tsk->completed);
1634
1635	while (1)
1636		if (down_interruptible (&tsk->sema) == 0) {
1637			unsigned long flags;
1638
1639			SMP_RD_BARRIER_DEPENDS();
1640			if (tsk->terminated) {
1641				break;
1642			}
1643
1644			dhd_os_sdlock(&dhd->pub);
1645			if (dhd->pub.dongle_reset == FALSE) {
1646				DHD_TIMER(("%s:\n", __FUNCTION__));
1647
1648				/* Call the bus module watchdog */
1649				dhd_bus_watchdog(&dhd->pub);
1650
1651				flags = dhd_os_spin_lock(&dhd->pub);
1652				/* Count the tick for reference */
1653				dhd->pub.tickcnt++;
1654				/* Reschedule the watchdog */
1655				if (dhd->wd_timer_valid)
1656					mod_timer(&dhd->timer,
1657					jiffies + dhd_watchdog_ms * HZ / 1000);
1658				dhd_os_spin_unlock(&dhd->pub, flags);
1659			}
1660			dhd_os_sdunlock(&dhd->pub);
1661			DHD_OS_WAKE_UNLOCK(&dhd->pub);
1662		} else {
1663			break;
1664		}
1665
1666	complete_and_exit(&tsk->completed, 0);
1667}
1668#endif /* DHDTHREAD */
1669
1670static void dhd_watchdog(ulong data)
1671{
1672	dhd_info_t *dhd = (dhd_info_t *)data;
1673	unsigned long flags;
1674
1675	DHD_OS_WAKE_LOCK(&dhd->pub);
1676	if (dhd->pub.dongle_reset) {
1677		DHD_OS_WAKE_UNLOCK(&dhd->pub);
1678		return;
1679	}
1680
1681#ifdef DHDTHREAD
1682	if (dhd->thr_wdt_ctl.thr_pid >= 0) {
1683		up(&dhd->thr_wdt_ctl.sema);
1684		return;
1685	}
1686#endif /* DHDTHREAD */
1687
1688	dhd_os_sdlock(&dhd->pub);
1689	/* Call the bus module watchdog */
1690	dhd_bus_watchdog(&dhd->pub);
1691
1692	flags = dhd_os_spin_lock(&dhd->pub);
1693	/* Count the tick for reference */
1694	dhd->pub.tickcnt++;
1695
1696	/* Reschedule the watchdog */
1697	if (dhd->wd_timer_valid)
1698		mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000);
1699	dhd_os_spin_unlock(&dhd->pub, flags);
1700	dhd_os_sdunlock(&dhd->pub);
1701	DHD_OS_WAKE_UNLOCK(&dhd->pub);
1702}
1703
1704#ifdef DHDTHREAD
1705static int
1706dhd_dpc_thread(void *data)
1707{
1708	tsk_ctl_t *tsk = (tsk_ctl_t *)data;
1709	dhd_info_t *dhd = (dhd_info_t *)tsk->parent;
1710
1711	/* This thread doesn't need any user-level access,
1712	 * so get rid of all our resources
1713	 */
1714	if (dhd_dpc_prio > 0)
1715	{
1716		struct sched_param param;
1717		param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1);
1718		setScheduler(current, SCHED_FIFO, &param);
1719	}
1720
1721	DAEMONIZE("dhd_dpc");
1722	/* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below  */
1723
1724	/*  signal: thread has started */
1725	complete(&tsk->completed);
1726
1727	/* Run until signal received */
1728	while (1) {
1729		if (down_interruptible(&tsk->sema) == 0) {
1730
1731			SMP_RD_BARRIER_DEPENDS();
1732			if (tsk->terminated) {
1733				break;
1734			}
1735
1736			/* Call bus dpc unless it indicated down (then clean stop) */
1737			if (dhd->pub.busstate != DHD_BUS_DOWN) {
1738				if (dhd_bus_dpc(dhd->pub.bus)) {
1739					up(&tsk->sema);
1740				}
1741				else {
1742					DHD_OS_WAKE_UNLOCK(&dhd->pub);
1743				}
1744			} else {
1745				dhd_bus_stop(dhd->pub.bus, TRUE);
1746				DHD_OS_WAKE_UNLOCK(&dhd->pub);
1747			}
1748		}
1749		else
1750			break;
1751	}
1752
1753	complete_and_exit(&tsk->completed, 0);
1754}
1755#endif /* DHDTHREAD */
1756
1757static void
1758dhd_dpc(ulong data)
1759{
1760	dhd_info_t *dhd;
1761
1762	dhd = (dhd_info_t *)data;
1763
1764	/* this (tasklet) can be scheduled in dhd_sched_dpc[dhd_linux.c]
1765	 * down below , wake lock is set,
1766	 * the tasklet is initialized in dhd_attach()
1767	 */
1768	/* Call bus dpc unless it indicated down (then clean stop) */
1769	if (dhd->pub.busstate != DHD_BUS_DOWN) {
1770		if (dhd_bus_dpc(dhd->pub.bus))
1771			tasklet_schedule(&dhd->tasklet);
1772		else
1773			DHD_OS_WAKE_UNLOCK(&dhd->pub);
1774	} else {
1775		dhd_bus_stop(dhd->pub.bus, TRUE);
1776		DHD_OS_WAKE_UNLOCK(&dhd->pub);
1777	}
1778}
1779
1780void
1781dhd_sched_dpc(dhd_pub_t *dhdp)
1782{
1783	dhd_info_t *dhd = (dhd_info_t *)dhdp->info;
1784
1785	DHD_OS_WAKE_LOCK(dhdp);
1786#ifdef DHDTHREAD
1787	if (dhd->thr_dpc_ctl.thr_pid >= 0) {
1788		up(&dhd->thr_dpc_ctl.sema);
1789		return;
1790	}
1791#endif /* DHDTHREAD */
1792
1793	tasklet_schedule(&dhd->tasklet);
1794}
1795
1796#ifdef TOE
1797/* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */
1798static int
1799dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol)
1800{
1801	wl_ioctl_t ioc;
1802	char buf[32];
1803	int ret;
1804
1805	memset(&ioc, 0, sizeof(ioc));
1806
1807	ioc.cmd = WLC_GET_VAR;
1808	ioc.buf = buf;
1809	ioc.len = (uint)sizeof(buf);
1810	ioc.set = FALSE;
1811
1812	strcpy(buf, "toe_ol");
1813	if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1814		/* Check for older dongle image that doesn't support toe_ol */
1815		if (ret == -EIO) {
1816			DHD_ERROR(("%s: toe not supported by device\n",
1817				dhd_ifname(&dhd->pub, ifidx)));
1818			return -EOPNOTSUPP;
1819		}
1820
1821		DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
1822		return ret;
1823	}
1824
1825	memcpy(toe_ol, buf, sizeof(uint32));
1826	return 0;
1827}
1828
1829/* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */
1830static int
1831dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol)
1832{
1833	wl_ioctl_t ioc;
1834	char buf[32];
1835	int toe, ret;
1836
1837	memset(&ioc, 0, sizeof(ioc));
1838
1839	ioc.cmd = WLC_SET_VAR;
1840	ioc.buf = buf;
1841	ioc.len = (uint)sizeof(buf);
1842	ioc.set = TRUE;
1843
1844	/* Set toe_ol as requested */
1845
1846	strcpy(buf, "toe_ol");
1847	memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32));
1848
1849	if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1850		DHD_ERROR(("%s: could not set toe_ol: ret=%d\n",
1851			dhd_ifname(&dhd->pub, ifidx), ret));
1852		return ret;
1853	}
1854
1855	/* Enable toe globally only if any components are enabled. */
1856
1857	toe = (toe_ol != 0);
1858
1859	strcpy(buf, "toe");
1860	memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32));
1861
1862	if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) {
1863		DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret));
1864		return ret;
1865	}
1866
1867	return 0;
1868}
1869#endif /* TOE */
1870
1871#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
1872static void
1873dhd_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
1874{
1875	dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
1876
1877	sprintf(info->driver, "wl");
1878	sprintf(info->version, "%lu", dhd->pub.drv_version);
1879}
1880
1881struct ethtool_ops dhd_ethtool_ops = {
1882	.get_drvinfo = dhd_ethtool_get_drvinfo
1883};
1884#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */
1885
1886
1887#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)
1888static int
1889dhd_ethtool(dhd_info_t *dhd, void *uaddr)
1890{
1891	struct ethtool_drvinfo info;
1892	char drvname[sizeof(info.driver)];
1893	uint32 cmd;
1894#ifdef TOE
1895	struct ethtool_value edata;
1896	uint32 toe_cmpnt, csum_dir;
1897	int ret;
1898#endif
1899
1900	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
1901
1902	/* all ethtool calls start with a cmd word */
1903	if (copy_from_user(&cmd, uaddr, sizeof (uint32)))
1904		return -EFAULT;
1905
1906	switch (cmd) {
1907	case ETHTOOL_GDRVINFO:
1908		/* Copy out any request driver name */
1909		if (copy_from_user(&info, uaddr, sizeof(info)))
1910			return -EFAULT;
1911		strncpy(drvname, info.driver, sizeof(info.driver));
1912		drvname[sizeof(info.driver)-1] = '\0';
1913
1914		/* clear struct for return */
1915		memset(&info, 0, sizeof(info));
1916		info.cmd = cmd;
1917
1918		/* if dhd requested, identify ourselves */
1919		if (strcmp(drvname, "?dhd") == 0) {
1920			sprintf(info.driver, "dhd");
1921			strcpy(info.version, EPI_VERSION_STR);
1922		}
1923
1924		/* otherwise, require dongle to be up */
1925		else if (!dhd->pub.up) {
1926			DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__));
1927			return -ENODEV;
1928		}
1929
1930		/* finally, report dongle driver type */
1931		else if (dhd->pub.iswl)
1932			sprintf(info.driver, "wl");
1933		else
1934			sprintf(info.driver, "xx");
1935
1936		sprintf(info.version, "%lu", dhd->pub.drv_version);
1937		if (copy_to_user(uaddr, &info, sizeof(info)))
1938			return -EFAULT;
1939		DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__,
1940		         (int)sizeof(drvname), drvname, info.driver));
1941		break;
1942
1943#ifdef TOE
1944	/* Get toe…

Large files files are truncated, but you can click here to view the full file