2010-02-16 01:44:14 +00:00
|
|
|
/*-
|
2017-11-27 14:52:40 +00:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
|
|
*
|
2010-02-16 01:44:14 +00:00
|
|
|
* Copyright (c) 2009-2010 Weongyo Jeong <weongyo@freebsd.org>
|
2018-02-05 23:38:15 +00:00
|
|
|
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
|
|
|
|
* Copyright (c) 2017 The FreeBSD Foundation
|
2010-02-16 01:44:14 +00:00
|
|
|
* All rights reserved.
|
2018-02-05 23:38:15 +00:00
|
|
|
*
|
|
|
|
* Portions of this software were developed by Landon Fuller
|
|
|
|
* under sponsorship from the FreeBSD Foundation.
|
2010-02-16 01:44:14 +00:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer,
|
|
|
|
* without modification.
|
|
|
|
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
|
|
|
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
|
|
|
* redistribution must be conditioned upon including a substantially
|
|
|
|
* similar Disclaimer requirement for further binary redistribution.
|
|
|
|
*
|
|
|
|
* NO WARRANTY
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
|
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
|
|
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
|
|
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
|
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
|
|
* THE POSSIBILITY OF SUCH DAMAGES.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The Broadcom Wireless LAN controller driver.
|
|
|
|
*/
|
|
|
|
|
2016-05-16 22:57:36 +00:00
|
|
|
#include "opt_bwn.h"
|
|
|
|
#include "opt_wlan.h"
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/kernel.h>
|
2018-02-05 23:38:15 +00:00
|
|
|
#include <sys/gpio.h>
|
2016-02-01 17:41:21 +00:00
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/module.h>
|
2010-02-16 01:44:14 +00:00
|
|
|
#include <sys/endian.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/firmware.h>
|
|
|
|
#include <sys/lock.h>
|
|
|
|
#include <sys/mutex.h>
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <machine/resource.h>
|
|
|
|
#include <sys/bus.h>
|
|
|
|
#include <sys/rman.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
|
|
|
|
#include <net/ethernet.h>
|
|
|
|
#include <net/if.h>
|
2013-10-26 17:58:36 +00:00
|
|
|
#include <net/if_var.h>
|
2010-02-16 01:44:14 +00:00
|
|
|
#include <net/if_arp.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/if_llc.h>
|
|
|
|
#include <net/if_media.h>
|
|
|
|
#include <net/if_types.h>
|
|
|
|
|
|
|
|
#include <net80211/ieee80211_var.h>
|
|
|
|
#include <net80211/ieee80211_radiotap.h>
|
|
|
|
#include <net80211/ieee80211_regdomain.h>
|
|
|
|
#include <net80211/ieee80211_phy.h>
|
2010-04-07 15:29:13 +00:00
|
|
|
#include <net80211/ieee80211_ratectl.h>
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
#include <dev/bhnd/bhnd.h>
|
|
|
|
#include <dev/bhnd/bhnd_ids.h>
|
|
|
|
|
|
|
|
#include <dev/bhnd/cores/chipc/chipc.h>
|
|
|
|
#include <dev/bhnd/cores/pmu/bhnd_pmu.h>
|
Introduce bwn(4) support for the bhnd(4) bus.
Currently, bwn(4) relies on the siba_bwn(4) bus driver to provide support
for the on-chip SSB interconnect found in Broadcom's older PCI(e) Wi-Fi
adapters. Non-PCI Wi-Fi adapters, as well as the newer BCMA interconnect
found in post-2009 Broadcom Wi-Fi hardware, are not supported by
siba_bwn(4).
The bhnd(4) bus driver (also used by the FreeBSD/MIPS Broadcom port)
provides a unified kernel interface to a superset of the hardware supported
by siba_bwn; by attaching bwn(4) via bhnd(4), we can support both modern
PCI(e) Wi-Fi devices based on the BCMA backplane interconnect, as well as
Broadcom MIPS WiSoCs that include a D11 MAC core directly attached to their
SSB or BCMA backplane.
This diff introduces opt-in bwn(4) support for bhnd(4) by providing:
- A small bwn(4) driver subclass, if_bwn_bhnd, that attaches via
bhnd(4) instead of siba_bwn(4).
- A bhndb(4)-based PCI host bridge driver, if_bwn_pci, that optionally
probes at a higher priority than the siba_bwn(4) PCI driver.
- A set of compatibility shims that perform translation of bwn(4)'s
siba_bwn function calls into their bhnd(9) API equivalents when bwn(4)
is attached via a bhnd(4) bus parent. When bwn(4) is attached via
siba_bwn(4), all siba_bwn function calls are simply passed through to
their original implementations.
To test bwn(4) with bhnd(4), place the following lines in loader.conf(5):
hw.bwn_pci.preferred="1"
if_bwn_pci_load="YES
bwn_v4_ucode_load="YES"
bwn_v4_lp_ucode_load="YES"
To verify that bwn(4) is using bhnd(4), you can check dmesg:
bwn0: <Broadcom 802.11 MAC/PHY/Radio, rev 15> ... on bhnd0
... or devinfo(8):
pcib2
pci2
bwn_pci0
bhndb0
bhnd0
bwn0
...
bwn(4)/bhnd(4) has been tested for regressions with most chipsets currently
supported by bwn(4), including:
- BCM4312
- BCM4318
- BCM4321
With minimal changes to the DMA code (not included in this commit), I was
also able to test support for newer BCMA devices by bringing up basic
working Wi-Fi on two previously unsupported, BCMA-based N-PHY chipsets:
- BCM43224
- BCM43225
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation & Plausible Labs
Differential Revision: https://reviews.freebsd.org/D13041
2017-12-02 02:21:27 +00:00
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
#include <dev/bwn/if_bwnreg.h>
|
|
|
|
#include <dev/bwn/if_bwnvar.h>
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
#include <dev/bwn/if_bwn_debug.h>
|
2016-05-02 21:06:02 +00:00
|
|
|
#include <dev/bwn/if_bwn_misc.h>
|
2016-05-14 23:20:46 +00:00
|
|
|
#include <dev/bwn/if_bwn_util.h>
|
|
|
|
#include <dev/bwn/if_bwn_phy_common.h>
|
2016-05-02 22:58:11 +00:00
|
|
|
#include <dev/bwn/if_bwn_phy_g.h>
|
2016-05-02 21:06:02 +00:00
|
|
|
#include <dev/bwn/if_bwn_phy_lp.h>
|
2016-05-17 07:12:00 +00:00
|
|
|
#include <dev/bwn/if_bwn_phy_n.h>
|
2016-05-02 21:06:02 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
#include "bhnd_nvram_map.h"
|
|
|
|
|
|
|
|
#include "gpio_if.h"
|
|
|
|
|
2011-11-07 15:43:11 +00:00
|
|
|
static SYSCTL_NODE(_hw, OID_AUTO, bwn, CTLFLAG_RD, 0,
|
|
|
|
"Broadcom driver parameters");
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Tunable & sysctl variables.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef BWN_DEBUG
|
|
|
|
static int bwn_debug = 0;
|
2014-06-28 03:56:17 +00:00
|
|
|
SYSCTL_INT(_hw_bwn, OID_AUTO, debug, CTLFLAG_RWTUN, &bwn_debug, 0,
|
2010-02-16 01:44:14 +00:00
|
|
|
"Broadcom debugging printfs");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int bwn_bfp = 0; /* use "Bad Frames Preemption" */
|
|
|
|
SYSCTL_INT(_hw_bwn, OID_AUTO, bfp, CTLFLAG_RW, &bwn_bfp, 0,
|
|
|
|
"uses Bad Frames Preemption");
|
|
|
|
static int bwn_bluetooth = 1;
|
|
|
|
SYSCTL_INT(_hw_bwn, OID_AUTO, bluetooth, CTLFLAG_RW, &bwn_bluetooth, 0,
|
|
|
|
"turns on Bluetooth Coexistence");
|
|
|
|
static int bwn_hwpctl = 0;
|
|
|
|
SYSCTL_INT(_hw_bwn, OID_AUTO, hwpctl, CTLFLAG_RW, &bwn_hwpctl, 0,
|
|
|
|
"uses H/W power control");
|
|
|
|
static int bwn_usedma = 1;
|
|
|
|
SYSCTL_INT(_hw_bwn, OID_AUTO, usedma, CTLFLAG_RD, &bwn_usedma, 0,
|
|
|
|
"uses DMA");
|
|
|
|
TUNABLE_INT("hw.bwn.usedma", &bwn_usedma);
|
|
|
|
static int bwn_wme = 1;
|
|
|
|
SYSCTL_INT(_hw_bwn, OID_AUTO, wme, CTLFLAG_RW, &bwn_wme, 0,
|
|
|
|
"uses WME support");
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
static void bwn_attach_pre(struct bwn_softc *);
|
2010-02-16 01:44:14 +00:00
|
|
|
static int bwn_attach_post(struct bwn_softc *);
|
2018-02-05 23:38:15 +00:00
|
|
|
static int bwn_retain_bus_providers(struct bwn_softc *sc);
|
|
|
|
static void bwn_release_bus_providers(struct bwn_softc *sc);
|
2010-03-09 19:58:00 +00:00
|
|
|
static void bwn_sprom_bugfixes(device_t);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
static int bwn_init(struct bwn_softc *);
|
|
|
|
static void bwn_parent(struct ieee80211com *);
|
|
|
|
static void bwn_start(struct bwn_softc *);
|
|
|
|
static int bwn_transmit(struct ieee80211com *, struct mbuf *);
|
2010-02-16 01:44:14 +00:00
|
|
|
static int bwn_attach_core(struct bwn_mac *);
|
|
|
|
static int bwn_phy_getinfo(struct bwn_mac *, int);
|
|
|
|
static int bwn_chiptest(struct bwn_mac *);
|
|
|
|
static int bwn_setup_channels(struct bwn_mac *, int, int);
|
|
|
|
static void bwn_shm_ctlword(struct bwn_mac *, uint16_t,
|
|
|
|
uint16_t);
|
|
|
|
static void bwn_addchannels(struct ieee80211_channel [], int, int *,
|
2016-05-16 22:39:17 +00:00
|
|
|
const struct bwn_channelinfo *, const uint8_t []);
|
2010-02-16 01:44:14 +00:00
|
|
|
static int bwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
|
|
|
|
const struct ieee80211_bpf_params *);
|
2015-05-25 19:53:29 +00:00
|
|
|
static void bwn_updateslot(struct ieee80211com *);
|
|
|
|
static void bwn_update_promisc(struct ieee80211com *);
|
2010-02-16 01:44:14 +00:00
|
|
|
static void bwn_wme_init(struct bwn_mac *);
|
|
|
|
static int bwn_wme_update(struct ieee80211com *);
|
|
|
|
static void bwn_wme_clear(struct bwn_softc *);
|
|
|
|
static void bwn_wme_load(struct bwn_mac *);
|
|
|
|
static void bwn_wme_loadparams(struct bwn_mac *,
|
|
|
|
const struct wmeParams *, uint16_t);
|
|
|
|
static void bwn_scan_start(struct ieee80211com *);
|
|
|
|
static void bwn_scan_end(struct ieee80211com *);
|
|
|
|
static void bwn_set_channel(struct ieee80211com *);
|
|
|
|
static struct ieee80211vap *bwn_vap_create(struct ieee80211com *,
|
2011-12-17 10:23:17 +00:00
|
|
|
const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
|
|
|
|
const uint8_t [IEEE80211_ADDR_LEN],
|
2010-02-16 01:44:14 +00:00
|
|
|
const uint8_t [IEEE80211_ADDR_LEN]);
|
|
|
|
static void bwn_vap_delete(struct ieee80211vap *);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
static void bwn_stop(struct bwn_softc *);
|
2018-02-05 23:38:15 +00:00
|
|
|
static int bwn_core_forceclk(struct bwn_mac *, bool);
|
2010-02-16 01:44:14 +00:00
|
|
|
static int bwn_core_init(struct bwn_mac *);
|
|
|
|
static void bwn_core_start(struct bwn_mac *);
|
|
|
|
static void bwn_core_exit(struct bwn_mac *);
|
|
|
|
static void bwn_bt_disable(struct bwn_mac *);
|
|
|
|
static int bwn_chip_init(struct bwn_mac *);
|
|
|
|
static void bwn_set_txretry(struct bwn_mac *, int, int);
|
|
|
|
static void bwn_rate_init(struct bwn_mac *);
|
|
|
|
static void bwn_set_phytxctl(struct bwn_mac *);
|
|
|
|
static void bwn_spu_setdelay(struct bwn_mac *, int);
|
|
|
|
static void bwn_bt_enable(struct bwn_mac *);
|
|
|
|
static void bwn_set_macaddr(struct bwn_mac *);
|
|
|
|
static void bwn_crypt_init(struct bwn_mac *);
|
|
|
|
static void bwn_chip_exit(struct bwn_mac *);
|
|
|
|
static int bwn_fw_fillinfo(struct bwn_mac *);
|
|
|
|
static int bwn_fw_loaducode(struct bwn_mac *);
|
|
|
|
static int bwn_gpio_init(struct bwn_mac *);
|
|
|
|
static int bwn_fw_loadinitvals(struct bwn_mac *);
|
|
|
|
static int bwn_phy_init(struct bwn_mac *);
|
|
|
|
static void bwn_set_txantenna(struct bwn_mac *, int);
|
|
|
|
static void bwn_set_opmode(struct bwn_mac *);
|
|
|
|
static void bwn_rate_write(struct bwn_mac *, uint16_t, int);
|
|
|
|
static uint8_t bwn_plcp_getcck(const uint8_t);
|
|
|
|
static uint8_t bwn_plcp_getofdm(const uint8_t);
|
|
|
|
static void bwn_pio_init(struct bwn_mac *);
|
|
|
|
static uint16_t bwn_pio_idx2base(struct bwn_mac *, int);
|
|
|
|
static void bwn_pio_set_txqueue(struct bwn_mac *, struct bwn_pio_txqueue *,
|
|
|
|
int);
|
|
|
|
static void bwn_pio_setupqueue_rx(struct bwn_mac *,
|
|
|
|
struct bwn_pio_rxqueue *, int);
|
|
|
|
static void bwn_destroy_queue_tx(struct bwn_pio_txqueue *);
|
|
|
|
static uint16_t bwn_pio_read_2(struct bwn_mac *, struct bwn_pio_txqueue *,
|
|
|
|
uint16_t);
|
|
|
|
static void bwn_pio_cancel_tx_packets(struct bwn_pio_txqueue *);
|
|
|
|
static int bwn_pio_rx(struct bwn_pio_rxqueue *);
|
|
|
|
static uint8_t bwn_pio_rxeof(struct bwn_pio_rxqueue *);
|
|
|
|
static void bwn_pio_handle_txeof(struct bwn_mac *,
|
|
|
|
const struct bwn_txstatus *);
|
|
|
|
static uint16_t bwn_pio_rx_read_2(struct bwn_pio_rxqueue *, uint16_t);
|
|
|
|
static uint32_t bwn_pio_rx_read_4(struct bwn_pio_rxqueue *, uint16_t);
|
|
|
|
static void bwn_pio_rx_write_2(struct bwn_pio_rxqueue *, uint16_t,
|
|
|
|
uint16_t);
|
|
|
|
static void bwn_pio_rx_write_4(struct bwn_pio_rxqueue *, uint16_t,
|
|
|
|
uint32_t);
|
|
|
|
static int bwn_pio_tx_start(struct bwn_mac *, struct ieee80211_node *,
|
|
|
|
struct mbuf *);
|
|
|
|
static struct bwn_pio_txqueue *bwn_pio_select(struct bwn_mac *, uint8_t);
|
|
|
|
static uint32_t bwn_pio_write_multi_4(struct bwn_mac *,
|
|
|
|
struct bwn_pio_txqueue *, uint32_t, const void *, int);
|
|
|
|
static void bwn_pio_write_4(struct bwn_mac *, struct bwn_pio_txqueue *,
|
|
|
|
uint16_t, uint32_t);
|
|
|
|
static uint16_t bwn_pio_write_multi_2(struct bwn_mac *,
|
|
|
|
struct bwn_pio_txqueue *, uint16_t, const void *, int);
|
|
|
|
static uint16_t bwn_pio_write_mbuf_2(struct bwn_mac *,
|
|
|
|
struct bwn_pio_txqueue *, uint16_t, struct mbuf *);
|
|
|
|
static struct bwn_pio_txqueue *bwn_pio_parse_cookie(struct bwn_mac *,
|
|
|
|
uint16_t, struct bwn_pio_txpkt **);
|
|
|
|
static void bwn_dma_init(struct bwn_mac *);
|
|
|
|
static void bwn_dma_rxdirectfifo(struct bwn_mac *, int, uint8_t);
|
|
|
|
static uint16_t bwn_dma_base(int, int);
|
|
|
|
static void bwn_dma_ringfree(struct bwn_dma_ring **);
|
|
|
|
static void bwn_dma_32_getdesc(struct bwn_dma_ring *,
|
|
|
|
int, struct bwn_dmadesc_generic **,
|
|
|
|
struct bwn_dmadesc_meta **);
|
|
|
|
static void bwn_dma_32_setdesc(struct bwn_dma_ring *,
|
|
|
|
struct bwn_dmadesc_generic *, bus_addr_t, uint16_t, int,
|
|
|
|
int, int);
|
|
|
|
static void bwn_dma_32_start_transfer(struct bwn_dma_ring *, int);
|
|
|
|
static void bwn_dma_32_suspend(struct bwn_dma_ring *);
|
|
|
|
static void bwn_dma_32_resume(struct bwn_dma_ring *);
|
|
|
|
static int bwn_dma_32_get_curslot(struct bwn_dma_ring *);
|
|
|
|
static void bwn_dma_32_set_curslot(struct bwn_dma_ring *, int);
|
|
|
|
static void bwn_dma_64_getdesc(struct bwn_dma_ring *,
|
|
|
|
int, struct bwn_dmadesc_generic **,
|
|
|
|
struct bwn_dmadesc_meta **);
|
|
|
|
static void bwn_dma_64_setdesc(struct bwn_dma_ring *,
|
|
|
|
struct bwn_dmadesc_generic *, bus_addr_t, uint16_t, int,
|
|
|
|
int, int);
|
|
|
|
static void bwn_dma_64_start_transfer(struct bwn_dma_ring *, int);
|
|
|
|
static void bwn_dma_64_suspend(struct bwn_dma_ring *);
|
|
|
|
static void bwn_dma_64_resume(struct bwn_dma_ring *);
|
|
|
|
static int bwn_dma_64_get_curslot(struct bwn_dma_ring *);
|
|
|
|
static void bwn_dma_64_set_curslot(struct bwn_dma_ring *, int);
|
|
|
|
static int bwn_dma_allocringmemory(struct bwn_dma_ring *);
|
|
|
|
static void bwn_dma_setup(struct bwn_dma_ring *);
|
|
|
|
static void bwn_dma_free_ringmemory(struct bwn_dma_ring *);
|
|
|
|
static void bwn_dma_cleanup(struct bwn_dma_ring *);
|
|
|
|
static void bwn_dma_free_descbufs(struct bwn_dma_ring *);
|
|
|
|
static int bwn_dma_tx_reset(struct bwn_mac *, uint16_t, int);
|
|
|
|
static void bwn_dma_rx(struct bwn_dma_ring *);
|
|
|
|
static int bwn_dma_rx_reset(struct bwn_mac *, uint16_t, int);
|
|
|
|
static void bwn_dma_free_descbuf(struct bwn_dma_ring *,
|
|
|
|
struct bwn_dmadesc_meta *);
|
|
|
|
static void bwn_dma_set_redzone(struct bwn_dma_ring *, struct mbuf *);
|
|
|
|
static void bwn_dma_ring_addr(void *, bus_dma_segment_t *, int, int);
|
|
|
|
static int bwn_dma_freeslot(struct bwn_dma_ring *);
|
|
|
|
static int bwn_dma_nextslot(struct bwn_dma_ring *, int);
|
|
|
|
static void bwn_dma_rxeof(struct bwn_dma_ring *, int *);
|
|
|
|
static int bwn_dma_newbuf(struct bwn_dma_ring *,
|
|
|
|
struct bwn_dmadesc_generic *, struct bwn_dmadesc_meta *,
|
|
|
|
int);
|
|
|
|
static void bwn_dma_buf_addr(void *, bus_dma_segment_t *, int,
|
|
|
|
bus_size_t, int);
|
|
|
|
static uint8_t bwn_dma_check_redzone(struct bwn_dma_ring *, struct mbuf *);
|
2016-10-02 20:35:55 +00:00
|
|
|
static void bwn_ratectl_tx_complete(const struct ieee80211_node *,
|
|
|
|
const struct bwn_txstatus *);
|
2010-02-16 01:44:14 +00:00
|
|
|
static void bwn_dma_handle_txeof(struct bwn_mac *,
|
|
|
|
const struct bwn_txstatus *);
|
|
|
|
static int bwn_dma_tx_start(struct bwn_mac *, struct ieee80211_node *,
|
|
|
|
struct mbuf *);
|
|
|
|
static int bwn_dma_getslot(struct bwn_dma_ring *);
|
|
|
|
static struct bwn_dma_ring *bwn_dma_select(struct bwn_mac *,
|
|
|
|
uint8_t);
|
|
|
|
static int bwn_dma_attach(struct bwn_mac *);
|
|
|
|
static struct bwn_dma_ring *bwn_dma_ringsetup(struct bwn_mac *,
|
2018-02-05 23:38:15 +00:00
|
|
|
int, int);
|
2010-02-16 01:44:14 +00:00
|
|
|
static struct bwn_dma_ring *bwn_dma_parse_cookie(struct bwn_mac *,
|
|
|
|
const struct bwn_txstatus *, uint16_t, int *);
|
|
|
|
static void bwn_dma_free(struct bwn_mac *);
|
|
|
|
static int bwn_fw_gets(struct bwn_mac *, enum bwn_fwtype);
|
|
|
|
static int bwn_fw_get(struct bwn_mac *, enum bwn_fwtype,
|
|
|
|
const char *, struct bwn_fwfile *);
|
|
|
|
static void bwn_release_firmware(struct bwn_mac *);
|
|
|
|
static void bwn_do_release_fw(struct bwn_fwfile *);
|
|
|
|
static uint16_t bwn_fwcaps_read(struct bwn_mac *);
|
|
|
|
static int bwn_fwinitvals_write(struct bwn_mac *,
|
|
|
|
const struct bwn_fwinitvals *, size_t, size_t);
|
|
|
|
static uint16_t bwn_ant2phy(int);
|
|
|
|
static void bwn_mac_write_bssid(struct bwn_mac *);
|
|
|
|
static void bwn_mac_setfilter(struct bwn_mac *, uint16_t,
|
|
|
|
const uint8_t *);
|
|
|
|
static void bwn_key_dowrite(struct bwn_mac *, uint8_t, uint8_t,
|
|
|
|
const uint8_t *, size_t, const uint8_t *);
|
|
|
|
static void bwn_key_macwrite(struct bwn_mac *, uint8_t,
|
|
|
|
const uint8_t *);
|
|
|
|
static void bwn_key_write(struct bwn_mac *, uint8_t, uint8_t,
|
|
|
|
const uint8_t *);
|
|
|
|
static void bwn_phy_exit(struct bwn_mac *);
|
|
|
|
static void bwn_core_stop(struct bwn_mac *);
|
|
|
|
static int bwn_switch_band(struct bwn_softc *,
|
|
|
|
struct ieee80211_channel *);
|
2018-02-05 23:38:15 +00:00
|
|
|
static int bwn_phy_reset(struct bwn_mac *);
|
2010-02-16 01:44:14 +00:00
|
|
|
static int bwn_newstate(struct ieee80211vap *, enum ieee80211_state, int);
|
|
|
|
static void bwn_set_pretbtt(struct bwn_mac *);
|
|
|
|
static int bwn_intr(void *);
|
|
|
|
static void bwn_intrtask(void *, int);
|
|
|
|
static void bwn_restart(struct bwn_mac *, const char *);
|
|
|
|
static void bwn_intr_ucode_debug(struct bwn_mac *);
|
|
|
|
static void bwn_intr_tbtt_indication(struct bwn_mac *);
|
|
|
|
static void bwn_intr_atim_end(struct bwn_mac *);
|
|
|
|
static void bwn_intr_beacon(struct bwn_mac *);
|
|
|
|
static void bwn_intr_pmq(struct bwn_mac *);
|
|
|
|
static void bwn_intr_noise(struct bwn_mac *);
|
|
|
|
static void bwn_intr_txeof(struct bwn_mac *);
|
|
|
|
static void bwn_hwreset(void *, int);
|
|
|
|
static void bwn_handle_fwpanic(struct bwn_mac *);
|
|
|
|
static void bwn_load_beacon0(struct bwn_mac *);
|
|
|
|
static void bwn_load_beacon1(struct bwn_mac *);
|
|
|
|
static uint32_t bwn_jssi_read(struct bwn_mac *);
|
|
|
|
static void bwn_noise_gensample(struct bwn_mac *);
|
|
|
|
static void bwn_handle_txeof(struct bwn_mac *,
|
|
|
|
const struct bwn_txstatus *);
|
|
|
|
static void bwn_rxeof(struct bwn_mac *, struct mbuf *, const void *);
|
|
|
|
static void bwn_phy_txpower_check(struct bwn_mac *, uint32_t);
|
|
|
|
static int bwn_tx_start(struct bwn_softc *, struct ieee80211_node *,
|
|
|
|
struct mbuf *);
|
|
|
|
static int bwn_tx_isfull(struct bwn_softc *, struct mbuf *);
|
|
|
|
static int bwn_set_txhdr(struct bwn_mac *,
|
|
|
|
struct ieee80211_node *, struct mbuf *, struct bwn_txhdr *,
|
|
|
|
uint16_t);
|
|
|
|
static void bwn_plcp_genhdr(struct bwn_plcp4 *, const uint16_t,
|
|
|
|
const uint8_t);
|
|
|
|
static uint8_t bwn_antenna_sanitize(struct bwn_mac *, uint8_t);
|
|
|
|
static uint8_t bwn_get_fbrate(uint8_t);
|
|
|
|
static void bwn_txpwr(void *, int);
|
|
|
|
static void bwn_tasks(void *);
|
|
|
|
static void bwn_task_15s(struct bwn_mac *);
|
|
|
|
static void bwn_task_30s(struct bwn_mac *);
|
|
|
|
static void bwn_task_60s(struct bwn_mac *);
|
|
|
|
static int bwn_plcp_get_ofdmrate(struct bwn_mac *, struct bwn_plcp6 *,
|
|
|
|
uint8_t);
|
|
|
|
static int bwn_plcp_get_cckrate(struct bwn_mac *, struct bwn_plcp6 *);
|
|
|
|
static void bwn_rx_radiotap(struct bwn_mac *, struct mbuf *,
|
|
|
|
const struct bwn_rxhdr4 *, struct bwn_plcp6 *, int,
|
|
|
|
int, int);
|
|
|
|
static void bwn_tsf_read(struct bwn_mac *, uint64_t *);
|
|
|
|
static void bwn_set_slot_time(struct bwn_mac *, uint16_t);
|
|
|
|
static void bwn_watchdog(void *);
|
|
|
|
static void bwn_dma_stop(struct bwn_mac *);
|
|
|
|
static void bwn_pio_stop(struct bwn_mac *);
|
|
|
|
static void bwn_dma_ringstop(struct bwn_dma_ring **);
|
2018-02-05 23:38:15 +00:00
|
|
|
static int bwn_led_attach(struct bwn_mac *);
|
2010-02-16 01:44:14 +00:00
|
|
|
static void bwn_led_newstate(struct bwn_mac *, enum ieee80211_state);
|
|
|
|
static void bwn_led_event(struct bwn_mac *, int);
|
|
|
|
static void bwn_led_blink_start(struct bwn_mac *, int, int);
|
|
|
|
static void bwn_led_blink_next(void *);
|
|
|
|
static void bwn_led_blink_end(void *);
|
|
|
|
static void bwn_rfswitch(void *);
|
|
|
|
static void bwn_rf_turnon(struct bwn_mac *);
|
|
|
|
static void bwn_rf_turnoff(struct bwn_mac *);
|
2010-02-23 19:55:54 +00:00
|
|
|
static void bwn_sysctl_node(struct bwn_softc *);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
static const struct bwn_channelinfo bwn_chantable_bg = {
|
|
|
|
.channels = {
|
|
|
|
{ 2412, 1, 30 }, { 2417, 2, 30 }, { 2422, 3, 30 },
|
|
|
|
{ 2427, 4, 30 }, { 2432, 5, 30 }, { 2437, 6, 30 },
|
|
|
|
{ 2442, 7, 30 }, { 2447, 8, 30 }, { 2452, 9, 30 },
|
|
|
|
{ 2457, 10, 30 }, { 2462, 11, 30 }, { 2467, 12, 30 },
|
|
|
|
{ 2472, 13, 30 }, { 2484, 14, 30 } },
|
|
|
|
.nchannels = 14
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct bwn_channelinfo bwn_chantable_a = {
|
|
|
|
.channels = {
|
|
|
|
{ 5170, 34, 30 }, { 5180, 36, 30 }, { 5190, 38, 30 },
|
|
|
|
{ 5200, 40, 30 }, { 5210, 42, 30 }, { 5220, 44, 30 },
|
|
|
|
{ 5230, 46, 30 }, { 5240, 48, 30 }, { 5260, 52, 30 },
|
|
|
|
{ 5280, 56, 30 }, { 5300, 60, 30 }, { 5320, 64, 30 },
|
|
|
|
{ 5500, 100, 30 }, { 5520, 104, 30 }, { 5540, 108, 30 },
|
|
|
|
{ 5560, 112, 30 }, { 5580, 116, 30 }, { 5600, 120, 30 },
|
|
|
|
{ 5620, 124, 30 }, { 5640, 128, 30 }, { 5660, 132, 30 },
|
|
|
|
{ 5680, 136, 30 }, { 5700, 140, 30 }, { 5745, 149, 30 },
|
|
|
|
{ 5765, 153, 30 }, { 5785, 157, 30 }, { 5805, 161, 30 },
|
|
|
|
{ 5825, 165, 30 }, { 5920, 184, 30 }, { 5940, 188, 30 },
|
|
|
|
{ 5960, 192, 30 }, { 5980, 196, 30 }, { 6000, 200, 30 },
|
|
|
|
{ 6020, 204, 30 }, { 6040, 208, 30 }, { 6060, 212, 30 },
|
|
|
|
{ 6080, 216, 30 } },
|
|
|
|
.nchannels = 37
|
|
|
|
};
|
|
|
|
|
2016-05-14 23:50:15 +00:00
|
|
|
#if 0
|
2010-02-16 01:44:14 +00:00
|
|
|
static const struct bwn_channelinfo bwn_chantable_n = {
|
|
|
|
.channels = {
|
|
|
|
{ 5160, 32, 30 }, { 5170, 34, 30 }, { 5180, 36, 30 },
|
|
|
|
{ 5190, 38, 30 }, { 5200, 40, 30 }, { 5210, 42, 30 },
|
|
|
|
{ 5220, 44, 30 }, { 5230, 46, 30 }, { 5240, 48, 30 },
|
|
|
|
{ 5250, 50, 30 }, { 5260, 52, 30 }, { 5270, 54, 30 },
|
|
|
|
{ 5280, 56, 30 }, { 5290, 58, 30 }, { 5300, 60, 30 },
|
|
|
|
{ 5310, 62, 30 }, { 5320, 64, 30 }, { 5330, 66, 30 },
|
|
|
|
{ 5340, 68, 30 }, { 5350, 70, 30 }, { 5360, 72, 30 },
|
|
|
|
{ 5370, 74, 30 }, { 5380, 76, 30 }, { 5390, 78, 30 },
|
|
|
|
{ 5400, 80, 30 }, { 5410, 82, 30 }, { 5420, 84, 30 },
|
|
|
|
{ 5430, 86, 30 }, { 5440, 88, 30 }, { 5450, 90, 30 },
|
|
|
|
{ 5460, 92, 30 }, { 5470, 94, 30 }, { 5480, 96, 30 },
|
|
|
|
{ 5490, 98, 30 }, { 5500, 100, 30 }, { 5510, 102, 30 },
|
|
|
|
{ 5520, 104, 30 }, { 5530, 106, 30 }, { 5540, 108, 30 },
|
|
|
|
{ 5550, 110, 30 }, { 5560, 112, 30 }, { 5570, 114, 30 },
|
|
|
|
{ 5580, 116, 30 }, { 5590, 118, 30 }, { 5600, 120, 30 },
|
|
|
|
{ 5610, 122, 30 }, { 5620, 124, 30 }, { 5630, 126, 30 },
|
|
|
|
{ 5640, 128, 30 }, { 5650, 130, 30 }, { 5660, 132, 30 },
|
|
|
|
{ 5670, 134, 30 }, { 5680, 136, 30 }, { 5690, 138, 30 },
|
|
|
|
{ 5700, 140, 30 }, { 5710, 142, 30 }, { 5720, 144, 30 },
|
|
|
|
{ 5725, 145, 30 }, { 5730, 146, 30 }, { 5735, 147, 30 },
|
|
|
|
{ 5740, 148, 30 }, { 5745, 149, 30 }, { 5750, 150, 30 },
|
|
|
|
{ 5755, 151, 30 }, { 5760, 152, 30 }, { 5765, 153, 30 },
|
|
|
|
{ 5770, 154, 30 }, { 5775, 155, 30 }, { 5780, 156, 30 },
|
|
|
|
{ 5785, 157, 30 }, { 5790, 158, 30 }, { 5795, 159, 30 },
|
|
|
|
{ 5800, 160, 30 }, { 5805, 161, 30 }, { 5810, 162, 30 },
|
|
|
|
{ 5815, 163, 30 }, { 5820, 164, 30 }, { 5825, 165, 30 },
|
|
|
|
{ 5830, 166, 30 }, { 5840, 168, 30 }, { 5850, 170, 30 },
|
|
|
|
{ 5860, 172, 30 }, { 5870, 174, 30 }, { 5880, 176, 30 },
|
|
|
|
{ 5890, 178, 30 }, { 5900, 180, 30 }, { 5910, 182, 30 },
|
|
|
|
{ 5920, 184, 30 }, { 5930, 186, 30 }, { 5940, 188, 30 },
|
|
|
|
{ 5950, 190, 30 }, { 5960, 192, 30 }, { 5970, 194, 30 },
|
|
|
|
{ 5980, 196, 30 }, { 5990, 198, 30 }, { 6000, 200, 30 },
|
|
|
|
{ 6010, 202, 30 }, { 6020, 204, 30 }, { 6030, 206, 30 },
|
|
|
|
{ 6040, 208, 30 }, { 6050, 210, 30 }, { 6060, 212, 30 },
|
|
|
|
{ 6070, 214, 30 }, { 6080, 216, 30 }, { 6090, 218, 30 },
|
|
|
|
{ 6100, 220, 30 }, { 6110, 222, 30 }, { 6120, 224, 30 },
|
|
|
|
{ 6130, 226, 30 }, { 6140, 228, 30 } },
|
|
|
|
.nchannels = 110
|
|
|
|
};
|
2016-05-14 23:50:15 +00:00
|
|
|
#endif
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
#define VENDOR_LED_ACT(vendor) \
|
|
|
|
{ \
|
|
|
|
.vid = PCI_VENDOR_##vendor, \
|
|
|
|
.led_act = { BWN_VENDOR_LED_ACT_##vendor } \
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
uint16_t vid;
|
|
|
|
uint8_t led_act[BWN_LED_MAX];
|
|
|
|
} bwn_vendor_led_act[] = {
|
2018-02-05 23:38:15 +00:00
|
|
|
VENDOR_LED_ACT(HP_COMPAQ),
|
2010-02-16 01:44:14 +00:00
|
|
|
VENDOR_LED_ACT(ASUSTEK)
|
|
|
|
};
|
|
|
|
|
|
|
|
static const uint8_t bwn_default_led_act[BWN_LED_MAX] =
|
|
|
|
{ BWN_VENDOR_LED_ACT_DEFAULT };
|
|
|
|
|
|
|
|
#undef VENDOR_LED_ACT
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
static const char *bwn_led_vars[] = {
|
|
|
|
BHND_NVAR_LEDBH0,
|
|
|
|
BHND_NVAR_LEDBH1,
|
|
|
|
BHND_NVAR_LEDBH2,
|
|
|
|
BHND_NVAR_LEDBH3
|
|
|
|
};
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
static const struct {
|
|
|
|
int on_dur;
|
|
|
|
int off_dur;
|
|
|
|
} bwn_led_duration[109] = {
|
|
|
|
[0] = { 400, 100 },
|
|
|
|
[2] = { 150, 75 },
|
|
|
|
[4] = { 90, 45 },
|
|
|
|
[11] = { 66, 34 },
|
|
|
|
[12] = { 53, 26 },
|
|
|
|
[18] = { 42, 21 },
|
|
|
|
[22] = { 35, 17 },
|
|
|
|
[24] = { 32, 16 },
|
|
|
|
[36] = { 21, 10 },
|
|
|
|
[48] = { 16, 8 },
|
|
|
|
[72] = { 11, 5 },
|
|
|
|
[96] = { 9, 4 },
|
|
|
|
[108] = { 7, 3 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const uint16_t bwn_wme_shm_offsets[] = {
|
|
|
|
[0] = BWN_WME_BESTEFFORT,
|
|
|
|
[1] = BWN_WME_BACKGROUND,
|
|
|
|
[2] = BWN_WME_VOICE,
|
|
|
|
[3] = BWN_WME_VIDEO,
|
|
|
|
};
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
/* Supported D11 core revisions */
|
|
|
|
#define BWN_DEV(_hwrev) {{ \
|
|
|
|
BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_D11), \
|
|
|
|
BHND_MATCH_CORE_REV(_hwrev), \
|
|
|
|
}}
|
|
|
|
static const struct bhnd_device bwn_devices[] = {
|
|
|
|
BWN_DEV(HWREV_RANGE(5, 16)),
|
|
|
|
BWN_DEV(HWREV_EQ(23)),
|
|
|
|
BHND_DEVICE_END
|
2010-02-16 01:44:14 +00:00
|
|
|
};
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
/* D11 quirks when bridged via a PCI host bridge core */
|
|
|
|
static const struct bhnd_device_quirk pci_bridge_quirks[] = {
|
|
|
|
BHND_CORE_QUIRK (HWREV_LTE(10), BWN_QUIRK_UCODE_SLOWCLOCK_WAR),
|
|
|
|
BHND_DEVICE_QUIRK_END
|
|
|
|
};
|
Introduce bwn(4) support for the bhnd(4) bus.
Currently, bwn(4) relies on the siba_bwn(4) bus driver to provide support
for the on-chip SSB interconnect found in Broadcom's older PCI(e) Wi-Fi
adapters. Non-PCI Wi-Fi adapters, as well as the newer BCMA interconnect
found in post-2009 Broadcom Wi-Fi hardware, are not supported by
siba_bwn(4).
The bhnd(4) bus driver (also used by the FreeBSD/MIPS Broadcom port)
provides a unified kernel interface to a superset of the hardware supported
by siba_bwn; by attaching bwn(4) via bhnd(4), we can support both modern
PCI(e) Wi-Fi devices based on the BCMA backplane interconnect, as well as
Broadcom MIPS WiSoCs that include a D11 MAC core directly attached to their
SSB or BCMA backplane.
This diff introduces opt-in bwn(4) support for bhnd(4) by providing:
- A small bwn(4) driver subclass, if_bwn_bhnd, that attaches via
bhnd(4) instead of siba_bwn(4).
- A bhndb(4)-based PCI host bridge driver, if_bwn_pci, that optionally
probes at a higher priority than the siba_bwn(4) PCI driver.
- A set of compatibility shims that perform translation of bwn(4)'s
siba_bwn function calls into their bhnd(9) API equivalents when bwn(4)
is attached via a bhnd(4) bus parent. When bwn(4) is attached via
siba_bwn(4), all siba_bwn function calls are simply passed through to
their original implementations.
To test bwn(4) with bhnd(4), place the following lines in loader.conf(5):
hw.bwn_pci.preferred="1"
if_bwn_pci_load="YES
bwn_v4_ucode_load="YES"
bwn_v4_lp_ucode_load="YES"
To verify that bwn(4) is using bhnd(4), you can check dmesg:
bwn0: <Broadcom 802.11 MAC/PHY/Radio, rev 15> ... on bhnd0
... or devinfo(8):
pcib2
pci2
bwn_pci0
bhndb0
bhnd0
bwn0
...
bwn(4)/bhnd(4) has been tested for regressions with most chipsets currently
supported by bwn(4), including:
- BCM4312
- BCM4318
- BCM4321
With minimal changes to the DMA code (not included in this commit), I was
also able to test support for newer BCMA devices by bringing up basic
working Wi-Fi on two previously unsupported, BCMA-based N-PHY chipsets:
- BCM43224
- BCM43225
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation & Plausible Labs
Differential Revision: https://reviews.freebsd.org/D13041
2017-12-02 02:21:27 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
/* D11 quirks when bridged via a PCMCIA host bridge core */
|
|
|
|
static const struct bhnd_device_quirk pcmcia_bridge_quirks[] = {
|
|
|
|
BHND_CORE_QUIRK (HWREV_ANY, BWN_QUIRK_NODMA),
|
|
|
|
BHND_DEVICE_QUIRK_END
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Host bridge cores for which D11 quirk flags should be applied */
|
|
|
|
static const struct bhnd_device bridge_devices[] = {
|
|
|
|
BHND_DEVICE(BCM, PCI, NULL, pci_bridge_quirks),
|
|
|
|
BHND_DEVICE(BCM, PCMCIA, NULL, pcmcia_bridge_quirks),
|
|
|
|
BHND_DEVICE_END
|
|
|
|
};
|
Introduce bwn(4) support for the bhnd(4) bus.
Currently, bwn(4) relies on the siba_bwn(4) bus driver to provide support
for the on-chip SSB interconnect found in Broadcom's older PCI(e) Wi-Fi
adapters. Non-PCI Wi-Fi adapters, as well as the newer BCMA interconnect
found in post-2009 Broadcom Wi-Fi hardware, are not supported by
siba_bwn(4).
The bhnd(4) bus driver (also used by the FreeBSD/MIPS Broadcom port)
provides a unified kernel interface to a superset of the hardware supported
by siba_bwn; by attaching bwn(4) via bhnd(4), we can support both modern
PCI(e) Wi-Fi devices based on the BCMA backplane interconnect, as well as
Broadcom MIPS WiSoCs that include a D11 MAC core directly attached to their
SSB or BCMA backplane.
This diff introduces opt-in bwn(4) support for bhnd(4) by providing:
- A small bwn(4) driver subclass, if_bwn_bhnd, that attaches via
bhnd(4) instead of siba_bwn(4).
- A bhndb(4)-based PCI host bridge driver, if_bwn_pci, that optionally
probes at a higher priority than the siba_bwn(4) PCI driver.
- A set of compatibility shims that perform translation of bwn(4)'s
siba_bwn function calls into their bhnd(9) API equivalents when bwn(4)
is attached via a bhnd(4) bus parent. When bwn(4) is attached via
siba_bwn(4), all siba_bwn function calls are simply passed through to
their original implementations.
To test bwn(4) with bhnd(4), place the following lines in loader.conf(5):
hw.bwn_pci.preferred="1"
if_bwn_pci_load="YES
bwn_v4_ucode_load="YES"
bwn_v4_lp_ucode_load="YES"
To verify that bwn(4) is using bhnd(4), you can check dmesg:
bwn0: <Broadcom 802.11 MAC/PHY/Radio, rev 15> ... on bhnd0
... or devinfo(8):
pcib2
pci2
bwn_pci0
bhndb0
bhnd0
bwn0
...
bwn(4)/bhnd(4) has been tested for regressions with most chipsets currently
supported by bwn(4), including:
- BCM4312
- BCM4318
- BCM4321
With minimal changes to the DMA code (not included in this commit), I was
also able to test support for newer BCMA devices by bringing up basic
working Wi-Fi on two previously unsupported, BCMA-based N-PHY chipsets:
- BCM43224
- BCM43225
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation & Plausible Labs
Differential Revision: https://reviews.freebsd.org/D13041
2017-12-02 02:21:27 +00:00
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
static int
|
|
|
|
bwn_probe(device_t dev)
|
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
const struct bhnd_device *id;
|
Introduce bwn(4) support for the bhnd(4) bus.
Currently, bwn(4) relies on the siba_bwn(4) bus driver to provide support
for the on-chip SSB interconnect found in Broadcom's older PCI(e) Wi-Fi
adapters. Non-PCI Wi-Fi adapters, as well as the newer BCMA interconnect
found in post-2009 Broadcom Wi-Fi hardware, are not supported by
siba_bwn(4).
The bhnd(4) bus driver (also used by the FreeBSD/MIPS Broadcom port)
provides a unified kernel interface to a superset of the hardware supported
by siba_bwn; by attaching bwn(4) via bhnd(4), we can support both modern
PCI(e) Wi-Fi devices based on the BCMA backplane interconnect, as well as
Broadcom MIPS WiSoCs that include a D11 MAC core directly attached to their
SSB or BCMA backplane.
This diff introduces opt-in bwn(4) support for bhnd(4) by providing:
- A small bwn(4) driver subclass, if_bwn_bhnd, that attaches via
bhnd(4) instead of siba_bwn(4).
- A bhndb(4)-based PCI host bridge driver, if_bwn_pci, that optionally
probes at a higher priority than the siba_bwn(4) PCI driver.
- A set of compatibility shims that perform translation of bwn(4)'s
siba_bwn function calls into their bhnd(9) API equivalents when bwn(4)
is attached via a bhnd(4) bus parent. When bwn(4) is attached via
siba_bwn(4), all siba_bwn function calls are simply passed through to
their original implementations.
To test bwn(4) with bhnd(4), place the following lines in loader.conf(5):
hw.bwn_pci.preferred="1"
if_bwn_pci_load="YES
bwn_v4_ucode_load="YES"
bwn_v4_lp_ucode_load="YES"
To verify that bwn(4) is using bhnd(4), you can check dmesg:
bwn0: <Broadcom 802.11 MAC/PHY/Radio, rev 15> ... on bhnd0
... or devinfo(8):
pcib2
pci2
bwn_pci0
bhndb0
bhnd0
bwn0
...
bwn(4)/bhnd(4) has been tested for regressions with most chipsets currently
supported by bwn(4), including:
- BCM4312
- BCM4318
- BCM4321
With minimal changes to the DMA code (not included in this commit), I was
also able to test support for newer BCMA devices by bringing up basic
working Wi-Fi on two previously unsupported, BCMA-based N-PHY chipsets:
- BCM43224
- BCM43225
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation & Plausible Labs
Differential Revision: https://reviews.freebsd.org/D13041
2017-12-02 02:21:27 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
id = bhnd_device_lookup(dev, bwn_devices, sizeof(bwn_devices[0]));
|
|
|
|
if (id == NULL)
|
|
|
|
return (ENXIO);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
bhnd_set_default_core_desc(dev);
|
|
|
|
return (BUS_PROBE_DEFAULT);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
static int
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_attach(device_t dev)
|
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
struct bwn_mac *mac;
|
|
|
|
struct bwn_softc *sc;
|
|
|
|
device_t parent, hostb;
|
|
|
|
char chip_name[BHND_CHIPID_MAX_NAMELEN];
|
|
|
|
int error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
sc = device_get_softc(dev);
|
2010-02-16 01:44:14 +00:00
|
|
|
sc->sc_dev = dev;
|
|
|
|
#ifdef BWN_DEBUG
|
|
|
|
sc->sc_debug = bwn_debug;
|
|
|
|
#endif
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
mac = NULL;
|
|
|
|
|
|
|
|
/* Determine the driver quirks applicable to this device, including any
|
|
|
|
* quirks specific to the bus host bridge core (if any) */
|
|
|
|
sc->sc_quirks = bhnd_device_quirks(dev, bwn_devices,
|
|
|
|
sizeof(bwn_devices[0]));
|
|
|
|
|
|
|
|
parent = device_get_parent(dev);
|
|
|
|
if ((hostb = bhnd_bus_find_hostb_device(parent)) != NULL) {
|
|
|
|
sc->sc_quirks |= bhnd_device_quirks(hostb, bridge_devices,
|
|
|
|
sizeof(bridge_devices[0]));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* DMA explicitly disabled? */
|
|
|
|
if (!bwn_usedma)
|
|
|
|
sc->sc_quirks |= BWN_QUIRK_NODMA;
|
|
|
|
|
|
|
|
/* Fetch our chip identification and board info */
|
|
|
|
sc->sc_cid = *bhnd_get_chipid(dev);
|
|
|
|
if ((error = bhnd_read_board_info(dev, &sc->sc_board_info))) {
|
|
|
|
device_printf(sc->sc_dev, "couldn't read board info\n");
|
Introduce bwn(4) support for the bhnd(4) bus.
Currently, bwn(4) relies on the siba_bwn(4) bus driver to provide support
for the on-chip SSB interconnect found in Broadcom's older PCI(e) Wi-Fi
adapters. Non-PCI Wi-Fi adapters, as well as the newer BCMA interconnect
found in post-2009 Broadcom Wi-Fi hardware, are not supported by
siba_bwn(4).
The bhnd(4) bus driver (also used by the FreeBSD/MIPS Broadcom port)
provides a unified kernel interface to a superset of the hardware supported
by siba_bwn; by attaching bwn(4) via bhnd(4), we can support both modern
PCI(e) Wi-Fi devices based on the BCMA backplane interconnect, as well as
Broadcom MIPS WiSoCs that include a D11 MAC core directly attached to their
SSB or BCMA backplane.
This diff introduces opt-in bwn(4) support for bhnd(4) by providing:
- A small bwn(4) driver subclass, if_bwn_bhnd, that attaches via
bhnd(4) instead of siba_bwn(4).
- A bhndb(4)-based PCI host bridge driver, if_bwn_pci, that optionally
probes at a higher priority than the siba_bwn(4) PCI driver.
- A set of compatibility shims that perform translation of bwn(4)'s
siba_bwn function calls into their bhnd(9) API equivalents when bwn(4)
is attached via a bhnd(4) bus parent. When bwn(4) is attached via
siba_bwn(4), all siba_bwn function calls are simply passed through to
their original implementations.
To test bwn(4) with bhnd(4), place the following lines in loader.conf(5):
hw.bwn_pci.preferred="1"
if_bwn_pci_load="YES
bwn_v4_ucode_load="YES"
bwn_v4_lp_ucode_load="YES"
To verify that bwn(4) is using bhnd(4), you can check dmesg:
bwn0: <Broadcom 802.11 MAC/PHY/Radio, rev 15> ... on bhnd0
... or devinfo(8):
pcib2
pci2
bwn_pci0
bhndb0
bhnd0
bwn0
...
bwn(4)/bhnd(4) has been tested for regressions with most chipsets currently
supported by bwn(4), including:
- BCM4312
- BCM4318
- BCM4321
With minimal changes to the DMA code (not included in this commit), I was
also able to test support for newer BCMA devices by bringing up basic
working Wi-Fi on two previously unsupported, BCMA-based N-PHY chipsets:
- BCM43224
- BCM43225
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation & Plausible Labs
Differential Revision: https://reviews.freebsd.org/D13041
2017-12-02 02:21:27 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
/* Allocate our D11 register block and PMU state */
|
|
|
|
sc->sc_mem_rid = 0;
|
|
|
|
sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
|
|
|
&sc->sc_mem_rid, RF_ACTIVE);
|
|
|
|
if (sc->sc_mem_res == NULL) {
|
|
|
|
device_printf(sc->sc_dev, "couldn't allocate registers\n");
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = bhnd_alloc_pmu(sc->sc_dev))) {
|
|
|
|
bus_release_resource(sc->sc_dev, SYS_RES_MEMORY,
|
|
|
|
sc->sc_mem_rid, sc->sc_mem_res);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Retain references to all required bus service providers */
|
|
|
|
if ((error = bwn_retain_bus_providers(sc)))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/* Fetch mask of available antennas */
|
|
|
|
error = bhnd_nvram_getvar_uint8(sc->sc_dev, BHND_NVAR_AA2G,
|
|
|
|
&sc->sc_ant2g);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "error determining 2GHz antenna "
|
|
|
|
"availability from NVRAM: %d\n", error);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bhnd_nvram_getvar_uint8(sc->sc_dev, BHND_NVAR_AA5G,
|
|
|
|
&sc->sc_ant5g);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "error determining 5GHz antenna "
|
|
|
|
"availability from NVRAM: %d\n", error);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
if ((sc->sc_flags & BWN_FLAG_ATTACHED) == 0) {
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
bwn_attach_pre(sc);
|
2010-03-09 19:58:00 +00:00
|
|
|
bwn_sprom_bugfixes(dev);
|
2010-02-16 01:44:14 +00:00
|
|
|
sc->sc_flags |= BWN_FLAG_ATTACHED;
|
|
|
|
}
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
mac = malloc(sizeof(*mac), M_DEVBUF, M_WAITOK | M_ZERO);
|
2010-02-16 01:44:14 +00:00
|
|
|
mac->mac_sc = sc;
|
|
|
|
mac->mac_status = BWN_MAC_STATUS_UNINIT;
|
|
|
|
if (bwn_bfp != 0)
|
|
|
|
mac->mac_flags |= BWN_MAC_FLAG_BADFRAME_PREEMP;
|
|
|
|
|
|
|
|
TASK_INIT(&mac->mac_hwreset, 0, bwn_hwreset, mac);
|
|
|
|
TASK_INIT(&mac->mac_intrtask, 0, bwn_intrtask, mac);
|
|
|
|
TASK_INIT(&mac->mac_txpower, 0, bwn_txpwr, mac);
|
|
|
|
|
|
|
|
error = bwn_attach_core(mac);
|
|
|
|
if (error)
|
2018-02-05 23:38:15 +00:00
|
|
|
goto fail;
|
|
|
|
error = bwn_led_attach(mac);
|
|
|
|
if (error)
|
|
|
|
goto fail;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
bhnd_format_chip_id(chip_name, sizeof(chip_name), sc->sc_cid.chip_id);
|
|
|
|
device_printf(sc->sc_dev, "WLAN (%s rev %u) "
|
2010-02-16 01:44:14 +00:00
|
|
|
"PHY (analog %d type %d rev %d) RADIO (manuf %#x ver %#x rev %d)\n",
|
2018-02-05 23:38:15 +00:00
|
|
|
chip_name, bhnd_get_hwrev(sc->sc_dev), mac->mac_phy.analog,
|
|
|
|
mac->mac_phy.type, mac->mac_phy.rev, mac->mac_phy.rf_manuf,
|
|
|
|
mac->mac_phy.rf_ver, mac->mac_phy.rf_rev);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (mac->mac_flags & BWN_MAC_FLAG_DMA)
|
2018-02-05 23:38:15 +00:00
|
|
|
device_printf(sc->sc_dev, "DMA (%d bits)\n", mac->mac_dmatype);
|
2010-02-16 01:44:14 +00:00
|
|
|
else
|
|
|
|
device_printf(sc->sc_dev, "PIO\n");
|
|
|
|
|
2016-05-17 20:21:39 +00:00
|
|
|
#ifdef BWN_GPL_PHY
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"Note: compiled with BWN_GPL_PHY; includes GPLv2 code\n");
|
|
|
|
#endif
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
mac->mac_rid_irq = 0;
|
|
|
|
mac->mac_res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
|
|
|
|
&mac->mac_rid_irq, RF_ACTIVE | RF_SHAREABLE);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (mac->mac_res_irq == NULL) {
|
|
|
|
device_printf(sc->sc_dev, "couldn't allocate IRQ resource\n");
|
|
|
|
error = ENXIO;
|
|
|
|
goto fail;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
error = bus_setup_intr(dev, mac->mac_res_irq,
|
|
|
|
INTR_TYPE_NET | INTR_MPSAFE, bwn_intr, NULL, mac,
|
|
|
|
&mac->mac_intrhand);
|
|
|
|
if (error != 0) {
|
|
|
|
device_printf(sc->sc_dev, "couldn't setup interrupt (%d)\n",
|
|
|
|
error);
|
|
|
|
goto fail;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TAILQ_INSERT_TAIL(&sc->sc_maclist, mac, mac_list);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* calls attach-post routine
|
|
|
|
*/
|
|
|
|
if ((sc->sc_flags & BWN_FLAG_ATTACHED) != 0)
|
|
|
|
bwn_attach_post(sc);
|
|
|
|
|
|
|
|
return (0);
|
2018-02-05 23:38:15 +00:00
|
|
|
fail:
|
|
|
|
if (mac != NULL && mac->mac_res_irq != NULL) {
|
|
|
|
bus_release_resource(dev, SYS_RES_IRQ, mac->mac_rid_irq,
|
|
|
|
mac->mac_res_irq);
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
free(mac, M_DEVBUF);
|
2018-02-05 23:38:15 +00:00
|
|
|
bhnd_release_pmu(dev);
|
|
|
|
bwn_release_bus_providers(sc);
|
|
|
|
|
|
|
|
if (sc->sc_mem_res != NULL) {
|
|
|
|
bus_release_resource(sc->sc_dev, SYS_RES_MEMORY,
|
|
|
|
sc->sc_mem_rid, sc->sc_mem_res);
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2018-02-05 23:38:15 +00:00
|
|
|
bwn_retain_bus_providers(struct bwn_softc *sc)
|
|
|
|
{
|
|
|
|
struct chipc_caps *ccaps;
|
|
|
|
|
|
|
|
sc->sc_chipc = bhnd_retain_provider(sc->sc_dev, BHND_SERVICE_CHIPC);
|
|
|
|
if (sc->sc_chipc == NULL) {
|
|
|
|
device_printf(sc->sc_dev, "ChipCommon device not found\n");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
ccaps = BHND_CHIPC_GET_CAPS(sc->sc_chipc);
|
|
|
|
|
|
|
|
sc->sc_gpio = bhnd_retain_provider(sc->sc_dev, BHND_SERVICE_GPIO);
|
|
|
|
if (sc->sc_gpio == NULL) {
|
|
|
|
device_printf(sc->sc_dev, "GPIO device not found\n");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ccaps->pmu) {
|
|
|
|
sc->sc_pmu = bhnd_retain_provider(sc->sc_dev, BHND_SERVICE_PMU);
|
|
|
|
if (sc->sc_pmu == NULL) {
|
|
|
|
device_printf(sc->sc_dev, "PMU device not found\n");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
failed:
|
|
|
|
bwn_release_bus_providers(sc);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_release_bus_providers(struct bwn_softc *sc)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
#define BWN_RELEASE_PROV(_sc, _prov, _service) do { \
|
|
|
|
if ((_sc)-> _prov != NULL) { \
|
|
|
|
bhnd_release_provider((_sc)->sc_dev, (_sc)-> _prov, \
|
|
|
|
(_service)); \
|
|
|
|
(_sc)-> _prov = NULL; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
BWN_RELEASE_PROV(sc, sc_chipc, BHND_SERVICE_CHIPC);
|
|
|
|
BWN_RELEASE_PROV(sc, sc_gpio, BHND_SERVICE_GPIO);
|
|
|
|
BWN_RELEASE_PROV(sc, sc_pmu, BHND_SERVICE_PMU);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
#undef BWN_RELEASE_PROV
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_attach_post(struct bwn_softc *sc)
|
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
struct ieee80211com *ic;
|
|
|
|
const char *mac_varname;
|
|
|
|
u_int core_unit;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
ic = &sc->sc_ic;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2015-05-25 18:50:26 +00:00
|
|
|
ic->ic_softc = sc;
|
2015-05-25 13:51:13 +00:00
|
|
|
ic->ic_name = device_get_nameunit(sc->sc_dev);
|
2010-02-16 01:44:14 +00:00
|
|
|
/* XXX not right but it's not used anywhere important */
|
|
|
|
ic->ic_phytype = IEEE80211_T_OFDM;
|
|
|
|
ic->ic_opmode = IEEE80211_M_STA;
|
|
|
|
ic->ic_caps =
|
|
|
|
IEEE80211_C_STA /* station mode supported */
|
|
|
|
| IEEE80211_C_MONITOR /* monitor mode */
|
2010-02-27 23:04:29 +00:00
|
|
|
| IEEE80211_C_AHDEMO /* adhoc demo mode */
|
2010-02-16 01:44:14 +00:00
|
|
|
| IEEE80211_C_SHPREAMBLE /* short preamble supported */
|
|
|
|
| IEEE80211_C_SHSLOT /* short slot time supported */
|
|
|
|
| IEEE80211_C_WME /* WME/WMM supported */
|
|
|
|
| IEEE80211_C_WPA /* capable of WPA1+WPA2 */
|
2016-05-14 23:10:47 +00:00
|
|
|
#if 0
|
2010-02-16 01:44:14 +00:00
|
|
|
| IEEE80211_C_BGSCAN /* capable of bg scanning */
|
2016-05-14 23:10:47 +00:00
|
|
|
#endif
|
2010-02-16 01:44:14 +00:00
|
|
|
| IEEE80211_C_TXPMGT /* capable of txpow mgt */
|
|
|
|
;
|
|
|
|
|
2010-03-14 02:13:11 +00:00
|
|
|
ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; /* s/w bmiss */
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
/* Determine the NVRAM variable containing our MAC address */
|
|
|
|
core_unit = bhnd_get_core_unit(sc->sc_dev);
|
|
|
|
mac_varname = NULL;
|
|
|
|
if (sc->sc_board_info.board_srom_rev <= 2) {
|
|
|
|
if (core_unit == 0) {
|
|
|
|
mac_varname = BHND_NVAR_IL0MACADDR;
|
|
|
|
} else if (core_unit == 1) {
|
|
|
|
mac_varname = BHND_NVAR_ET1MACADDR;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (core_unit == 0) {
|
|
|
|
mac_varname = BHND_NVAR_MACADDR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mac_varname == NULL) {
|
|
|
|
device_printf(sc->sc_dev, "missing MAC address variable for "
|
|
|
|
"D11 core %u", core_unit);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the MAC address from NVRAM */
|
|
|
|
error = bhnd_nvram_getvar_array(sc->sc_dev, mac_varname, ic->ic_macaddr,
|
|
|
|
sizeof(ic->ic_macaddr), BHND_NVRAM_TYPE_UINT8_ARRAY);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "error reading %s: %d\n", mac_varname,
|
|
|
|
error);
|
|
|
|
return (error);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
/* call MI attach routine. */
|
|
|
|
ieee80211_ifattach(ic);
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
ic->ic_headroom = sizeof(struct bwn_txhdr);
|
|
|
|
|
|
|
|
/* override default methods */
|
|
|
|
ic->ic_raw_xmit = bwn_raw_xmit;
|
|
|
|
ic->ic_updateslot = bwn_updateslot;
|
|
|
|
ic->ic_update_promisc = bwn_update_promisc;
|
|
|
|
ic->ic_wme.wme_update = bwn_wme_update;
|
|
|
|
ic->ic_scan_start = bwn_scan_start;
|
|
|
|
ic->ic_scan_end = bwn_scan_end;
|
|
|
|
ic->ic_set_channel = bwn_set_channel;
|
|
|
|
ic->ic_vap_create = bwn_vap_create;
|
|
|
|
ic->ic_vap_delete = bwn_vap_delete;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
ic->ic_transmit = bwn_transmit;
|
|
|
|
ic->ic_parent = bwn_parent;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
ieee80211_radiotap_attach(ic,
|
|
|
|
&sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th),
|
|
|
|
BWN_TX_RADIOTAP_PRESENT,
|
|
|
|
&sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th),
|
|
|
|
BWN_RX_RADIOTAP_PRESENT);
|
|
|
|
|
2010-02-23 19:55:54 +00:00
|
|
|
bwn_sysctl_node(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
if (bootverbose)
|
|
|
|
ieee80211_announce(ic);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_phy_detach(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (mac->mac_phy.detach != NULL)
|
|
|
|
mac->mac_phy.detach(mac);
|
|
|
|
}
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
static int
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_detach(device_t dev)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = device_get_softc(dev);
|
|
|
|
struct bwn_mac *mac = sc->sc_curmac;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
sc->sc_flags |= BWN_FLAG_INVALID;
|
|
|
|
|
|
|
|
if (device_is_attached(sc->sc_dev)) {
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
BWN_LOCK(sc);
|
|
|
|
bwn_stop(sc);
|
|
|
|
BWN_UNLOCK(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_dma_free(mac);
|
|
|
|
callout_drain(&sc->sc_led_blink_ch);
|
|
|
|
callout_drain(&sc->sc_rfswitch_ch);
|
|
|
|
callout_drain(&sc->sc_task_ch);
|
|
|
|
callout_drain(&sc->sc_watchdog_ch);
|
|
|
|
bwn_phy_detach(mac);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
ieee80211_draintask(ic, &mac->mac_hwreset);
|
|
|
|
ieee80211_draintask(ic, &mac->mac_txpower);
|
|
|
|
ieee80211_ifdetach(ic);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
taskqueue_drain(sc->sc_tq, &mac->mac_intrtask);
|
|
|
|
taskqueue_free(sc->sc_tq);
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (mac->mac_intrhand != NULL) {
|
|
|
|
bus_teardown_intr(dev, mac->mac_res_irq, mac->mac_intrhand);
|
|
|
|
mac->mac_intrhand = NULL;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2018-02-05 23:38:15 +00:00
|
|
|
|
|
|
|
bhnd_release_pmu(dev);
|
|
|
|
bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
|
|
|
|
sc->sc_mem_res);
|
|
|
|
bus_release_resource(dev, SYS_RES_IRQ, mac->mac_rid_irq,
|
|
|
|
mac->mac_res_irq);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
mbufq_drain(&sc->sc_snd);
|
2016-06-05 07:56:28 +00:00
|
|
|
bwn_release_firmware(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
BWN_LOCK_DESTROY(sc);
|
2018-02-05 23:38:15 +00:00
|
|
|
|
|
|
|
bwn_release_bus_providers(sc);
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
static void
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_attach_pre(struct bwn_softc *sc)
|
|
|
|
{
|
|
|
|
|
|
|
|
BWN_LOCK_INIT(sc);
|
|
|
|
TAILQ_INIT(&sc->sc_maclist);
|
|
|
|
callout_init_mtx(&sc->sc_rfswitch_ch, &sc->sc_mtx, 0);
|
|
|
|
callout_init_mtx(&sc->sc_task_ch, &sc->sc_mtx, 0);
|
|
|
|
callout_init_mtx(&sc->sc_watchdog_ch, &sc->sc_mtx, 0);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
mbufq_init(&sc->sc_snd, ifqmaxlen);
|
2010-02-16 01:44:14 +00:00
|
|
|
sc->sc_tq = taskqueue_create_fast("bwn_taskq", M_NOWAIT,
|
|
|
|
taskqueue_thread_enqueue, &sc->sc_tq);
|
|
|
|
taskqueue_start_threads(&sc->sc_tq, 1, PI_NET,
|
|
|
|
"%s taskq", device_get_nameunit(sc->sc_dev));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2010-03-09 19:58:00 +00:00
|
|
|
bwn_sprom_bugfixes(device_t dev)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
struct bwn_softc *sc = device_get_softc(dev);
|
|
|
|
|
|
|
|
#define BWN_ISDEV(_device, _subvendor, _subdevice) \
|
|
|
|
((sc->sc_board_info.board_devid == PCI_DEVID_##_device) && \
|
|
|
|
(sc->sc_board_info.board_vendor == PCI_VENDOR_##_subvendor) && \
|
|
|
|
(sc->sc_board_info.board_type == _subdevice))
|
|
|
|
|
|
|
|
/* A subset of Apple Airport Extreme (BCM4306 rev 2) devices
|
|
|
|
* were programmed with a missing PACTRL boardflag */
|
|
|
|
if (sc->sc_board_info.board_vendor == PCI_VENDOR_APPLE &&
|
|
|
|
sc->sc_board_info.board_type == 0x4e &&
|
|
|
|
sc->sc_board_info.board_rev > 0x40)
|
|
|
|
sc->sc_board_info.board_flags |= BHND_BFL_PACTRL;
|
|
|
|
|
|
|
|
if (BWN_ISDEV(BCM4318_D11G, ASUSTEK, 0x100f) ||
|
|
|
|
BWN_ISDEV(BCM4306_D11G, DELL, 0x0003) ||
|
|
|
|
BWN_ISDEV(BCM4306_D11G, HP, 0x12f8) ||
|
|
|
|
BWN_ISDEV(BCM4306_D11G, LINKSYS, 0x0013) ||
|
|
|
|
BWN_ISDEV(BCM4306_D11G, LINKSYS, 0x0014) ||
|
|
|
|
BWN_ISDEV(BCM4306_D11G, LINKSYS, 0x0015) ||
|
|
|
|
BWN_ISDEV(BCM4306_D11G, MOTOROLA, 0x7010))
|
|
|
|
sc->sc_board_info.board_flags &= ~BHND_BFL_BTCOEX;
|
2010-02-16 01:44:14 +00:00
|
|
|
#undef BWN_ISDEV
|
|
|
|
}
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
static void
|
|
|
|
bwn_parent(struct ieee80211com *ic)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = ic->ic_softc;
|
|
|
|
int startall = 0;
|
|
|
|
|
|
|
|
BWN_LOCK(sc);
|
|
|
|
if (ic->ic_nrunning > 0) {
|
|
|
|
if ((sc->sc_flags & BWN_FLAG_RUNNING) == 0) {
|
|
|
|
bwn_init(sc);
|
|
|
|
startall = 1;
|
2015-08-08 01:10:17 +00:00
|
|
|
} else
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
bwn_update_promisc(ic);
|
|
|
|
} else if (sc->sc_flags & BWN_FLAG_RUNNING)
|
|
|
|
bwn_stop(sc);
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
|
|
|
|
if (startall)
|
|
|
|
ieee80211_start_all(ic);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
static int
|
|
|
|
bwn_transmit(struct ieee80211com *ic, struct mbuf *m)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct bwn_softc *sc = ic->ic_softc;
|
|
|
|
int error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
BWN_LOCK(sc);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
if ((sc->sc_flags & BWN_FLAG_RUNNING) == 0) {
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
error = mbufq_enqueue(&sc->sc_snd, m);
|
|
|
|
if (error) {
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
bwn_start(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
BWN_UNLOCK(sc);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
return (0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
bwn_start(struct bwn_softc *sc)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
struct bwn_mac *mac = sc->sc_curmac;
|
|
|
|
struct ieee80211_frame *wh;
|
|
|
|
struct ieee80211_node *ni;
|
|
|
|
struct ieee80211_key *k;
|
|
|
|
struct mbuf *m;
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
if ((sc->sc_flags & BWN_FLAG_RUNNING) == 0 || mac == NULL ||
|
2010-02-16 01:44:14 +00:00
|
|
|
mac->mac_status < BWN_MAC_STATUS_STARTED)
|
|
|
|
return;
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
|
2010-02-16 01:44:14 +00:00
|
|
|
if (bwn_tx_isfull(sc, m))
|
|
|
|
break;
|
|
|
|
ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
|
|
|
|
if (ni == NULL) {
|
|
|
|
device_printf(sc->sc_dev, "unexpected NULL ni\n");
|
|
|
|
m_freem(m);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
counter_u64_add(sc->sc_ic.ic_oerrors, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
wh = mtod(m, struct ieee80211_frame *);
|
2014-01-08 08:06:56 +00:00
|
|
|
if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
|
2010-02-16 01:44:14 +00:00
|
|
|
k = ieee80211_crypto_encap(ni, m);
|
|
|
|
if (k == NULL) {
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
if_inc_counter(ni->ni_vap->iv_ifp,
|
|
|
|
IFCOUNTER_OERRORS, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
ieee80211_free_node(ni);
|
|
|
|
m_freem(m);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wh = NULL; /* Catch any invalid use */
|
|
|
|
if (bwn_tx_start(sc, ni, m) != 0) {
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
if (ni != NULL) {
|
|
|
|
if_inc_counter(ni->ni_vap->iv_ifp,
|
|
|
|
IFCOUNTER_OERRORS, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
ieee80211_free_node(ni);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sc->sc_watchdog_timer = 5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_tx_isfull(struct bwn_softc *sc, struct mbuf *m)
|
|
|
|
{
|
|
|
|
struct bwn_dma_ring *dr;
|
|
|
|
struct bwn_mac *mac = sc->sc_curmac;
|
|
|
|
struct bwn_pio_txqueue *tq;
|
|
|
|
int pktlen = roundup(m->m_pkthdr.len + BWN_HDRSIZE(mac), 4);
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
|
|
|
|
if (mac->mac_flags & BWN_MAC_FLAG_DMA) {
|
|
|
|
dr = bwn_dma_select(mac, M_WME_GETAC(m));
|
|
|
|
if (dr->dr_stop == 1 ||
|
|
|
|
bwn_dma_freeslot(dr) < BWN_TX_SLOTS_PER_FRAME) {
|
|
|
|
dr->dr_stop = 1;
|
|
|
|
goto full;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tq = bwn_pio_select(mac, M_WME_GETAC(m));
|
|
|
|
if (tq->tq_free == 0 || pktlen > tq->tq_size ||
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
pktlen > (tq->tq_size - tq->tq_used))
|
2010-02-16 01:44:14 +00:00
|
|
|
goto full;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
full:
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
mbufq_prepend(&sc->sc_snd, m);
|
2010-02-16 01:44:14 +00:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_tx_start(struct bwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = sc->sc_curmac;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
|
|
|
|
if (m->m_pkthdr.len < IEEE80211_MIN_LEN || mac == NULL) {
|
|
|
|
m_freem(m);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
error = (mac->mac_flags & BWN_MAC_FLAG_DMA) ?
|
|
|
|
bwn_dma_tx_start(mac, ni, m) : bwn_pio_tx_start(mac, ni, m);
|
|
|
|
if (error) {
|
|
|
|
m_freem(m);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_pio_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni, struct mbuf *m)
|
|
|
|
{
|
|
|
|
struct bwn_pio_txpkt *tp;
|
|
|
|
struct bwn_pio_txqueue *tq = bwn_pio_select(mac, M_WME_GETAC(m));
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
struct bwn_txhdr txhdr;
|
|
|
|
struct mbuf *m_new;
|
|
|
|
uint32_t ctl32;
|
|
|
|
int error;
|
|
|
|
uint16_t ctl16;
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
|
|
|
|
/* XXX TODO send packets after DTIM */
|
|
|
|
|
|
|
|
KASSERT(!TAILQ_EMPTY(&tq->tq_pktlist), ("%s: fail", __func__));
|
|
|
|
tp = TAILQ_FIRST(&tq->tq_pktlist);
|
|
|
|
tp->tp_ni = ni;
|
|
|
|
tp->tp_m = m;
|
|
|
|
|
|
|
|
error = bwn_set_txhdr(mac, ni, m, &txhdr, BWN_PIO_COOKIE(tq, tp));
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "tx fail\n");
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
TAILQ_REMOVE(&tq->tq_pktlist, tp, tp_list);
|
|
|
|
tq->tq_used += roundup(m->m_pkthdr.len + BWN_HDRSIZE(mac), 4);
|
|
|
|
tq->tq_free--;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (bhnd_get_hwrev(sc->sc_dev) >= 8) {
|
2010-02-16 01:44:14 +00:00
|
|
|
/*
|
|
|
|
* XXX please removes m_defrag(9)
|
|
|
|
*/
|
2012-12-04 09:32:43 +00:00
|
|
|
m_new = m_defrag(m, M_NOWAIT);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (m_new == NULL) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"%s: can't defrag TX buffer\n",
|
|
|
|
__func__);
|
|
|
|
return (ENOBUFS);
|
|
|
|
}
|
|
|
|
if (m_new->m_next != NULL)
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"TODO: fragmented packets for PIO\n");
|
|
|
|
tp->tp_m = m_new;
|
|
|
|
|
|
|
|
/* send HEADER */
|
|
|
|
ctl32 = bwn_pio_write_multi_4(mac, tq,
|
|
|
|
(BWN_PIO_READ_4(mac, tq, BWN_PIO8_TXCTL) |
|
|
|
|
BWN_PIO8_TXCTL_FRAMEREADY) & ~BWN_PIO8_TXCTL_EOF,
|
|
|
|
(const uint8_t *)&txhdr, BWN_HDRSIZE(mac));
|
|
|
|
/* send BODY */
|
|
|
|
ctl32 = bwn_pio_write_multi_4(mac, tq, ctl32,
|
|
|
|
mtod(m_new, const void *), m_new->m_pkthdr.len);
|
|
|
|
bwn_pio_write_4(mac, tq, BWN_PIO_TXCTL,
|
|
|
|
ctl32 | BWN_PIO8_TXCTL_EOF);
|
|
|
|
} else {
|
|
|
|
ctl16 = bwn_pio_write_multi_2(mac, tq,
|
|
|
|
(bwn_pio_read_2(mac, tq, BWN_PIO_TXCTL) |
|
|
|
|
BWN_PIO_TXCTL_FRAMEREADY) & ~BWN_PIO_TXCTL_EOF,
|
|
|
|
(const uint8_t *)&txhdr, BWN_HDRSIZE(mac));
|
|
|
|
ctl16 = bwn_pio_write_mbuf_2(mac, tq, ctl16, m);
|
|
|
|
BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXCTL,
|
|
|
|
ctl16 | BWN_PIO_TXCTL_EOF);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bwn_pio_txqueue *
|
|
|
|
bwn_pio_select(struct bwn_mac *mac, uint8_t prio)
|
|
|
|
{
|
|
|
|
|
|
|
|
if ((mac->mac_flags & BWN_MAC_FLAG_WME) == 0)
|
|
|
|
return (&mac->mac_method.pio.wme[WME_AC_BE]);
|
|
|
|
|
|
|
|
switch (prio) {
|
|
|
|
case 0:
|
|
|
|
return (&mac->mac_method.pio.wme[WME_AC_BE]);
|
|
|
|
case 1:
|
|
|
|
return (&mac->mac_method.pio.wme[WME_AC_BK]);
|
|
|
|
case 2:
|
|
|
|
return (&mac->mac_method.pio.wme[WME_AC_VI]);
|
|
|
|
case 3:
|
|
|
|
return (&mac->mac_method.pio.wme[WME_AC_VO]);
|
|
|
|
}
|
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
2010-02-23 05:02:10 +00:00
|
|
|
return (NULL);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_dma_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni, struct mbuf *m)
|
|
|
|
{
|
|
|
|
#define BWN_GET_TXHDRCACHE(slot) \
|
|
|
|
&(txhdr_cache[(slot / BWN_TX_SLOTS_PER_FRAME) * BWN_HDRSIZE(mac)])
|
|
|
|
struct bwn_dma *dma = &mac->mac_method.dma;
|
|
|
|
struct bwn_dma_ring *dr = bwn_dma_select(mac, M_WME_GETAC(m));
|
|
|
|
struct bwn_dmadesc_generic *desc;
|
|
|
|
struct bwn_dmadesc_meta *mt;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint8_t *txhdr_cache = (uint8_t *)dr->dr_txhdr_cache;
|
|
|
|
int error, slot, backup[2] = { dr->dr_curslot, dr->dr_usedslot };
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
KASSERT(!dr->dr_stop, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
|
|
|
/* XXX send after DTIM */
|
|
|
|
|
|
|
|
slot = bwn_dma_getslot(dr);
|
|
|
|
dr->getdesc(dr, slot, &desc, &mt);
|
|
|
|
KASSERT(mt->mt_txtype == BWN_DMADESC_METATYPE_HEADER,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
|
|
|
error = bwn_set_txhdr(dr->dr_mac, ni, m,
|
|
|
|
(struct bwn_txhdr *)BWN_GET_TXHDRCACHE(slot),
|
|
|
|
BWN_DMA_COOKIE(dr, slot));
|
|
|
|
if (error)
|
|
|
|
goto fail;
|
|
|
|
error = bus_dmamap_load(dr->dr_txring_dtag, mt->mt_dmap,
|
|
|
|
BWN_GET_TXHDRCACHE(slot), BWN_HDRSIZE(mac), bwn_dma_ring_addr,
|
|
|
|
&mt->mt_paddr, BUS_DMA_NOWAIT);
|
|
|
|
if (error) {
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
device_printf(sc->sc_dev, "%s: can't load TX buffer (1) %d\n",
|
2010-02-16 01:44:14 +00:00
|
|
|
__func__, error);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
bus_dmamap_sync(dr->dr_txring_dtag, mt->mt_dmap,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
dr->setdesc(dr, desc, mt->mt_paddr, BWN_HDRSIZE(mac), 1, 0, 0);
|
|
|
|
bus_dmamap_sync(dr->dr_ring_dtag, dr->dr_ring_dmap,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
slot = bwn_dma_getslot(dr);
|
|
|
|
dr->getdesc(dr, slot, &desc, &mt);
|
|
|
|
KASSERT(mt->mt_txtype == BWN_DMADESC_METATYPE_BODY &&
|
|
|
|
mt->mt_islast == 1, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
mt->mt_m = m;
|
|
|
|
mt->mt_ni = ni;
|
|
|
|
|
|
|
|
error = bus_dmamap_load_mbuf(dma->txbuf_dtag, mt->mt_dmap, m,
|
|
|
|
bwn_dma_buf_addr, &mt->mt_paddr, BUS_DMA_NOWAIT);
|
|
|
|
if (error && error != EFBIG) {
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
device_printf(sc->sc_dev, "%s: can't load TX buffer (1) %d\n",
|
2010-02-16 01:44:14 +00:00
|
|
|
__func__, error);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (error) { /* error == EFBIG */
|
|
|
|
struct mbuf *m_new;
|
|
|
|
|
2012-12-04 09:32:43 +00:00
|
|
|
m_new = m_defrag(m, M_NOWAIT);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (m_new == NULL) {
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"%s: can't defrag TX buffer\n",
|
2010-02-16 01:44:14 +00:00
|
|
|
__func__);
|
|
|
|
error = ENOBUFS;
|
|
|
|
goto fail;
|
|
|
|
} else {
|
|
|
|
m = m_new;
|
|
|
|
}
|
|
|
|
|
|
|
|
mt->mt_m = m;
|
|
|
|
error = bus_dmamap_load_mbuf(dma->txbuf_dtag, mt->mt_dmap,
|
|
|
|
m, bwn_dma_buf_addr, &mt->mt_paddr, BUS_DMA_NOWAIT);
|
|
|
|
if (error) {
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"%s: can't load TX buffer (2) %d\n",
|
2010-02-16 01:44:14 +00:00
|
|
|
__func__, error);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bus_dmamap_sync(dma->txbuf_dtag, mt->mt_dmap, BUS_DMASYNC_PREWRITE);
|
|
|
|
dr->setdesc(dr, desc, mt->mt_paddr, m->m_pkthdr.len, 0, 1, 1);
|
|
|
|
bus_dmamap_sync(dr->dr_ring_dtag, dr->dr_ring_dmap,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
/* XXX send after DTIM */
|
|
|
|
|
|
|
|
dr->start_transfer(dr, bwn_dma_nextslot(dr, slot));
|
|
|
|
return (0);
|
|
|
|
fail:
|
|
|
|
dr->dr_curslot = backup[0];
|
|
|
|
dr->dr_usedslot = backup[1];
|
|
|
|
return (error);
|
|
|
|
#undef BWN_GET_TXHDRCACHE
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_watchdog(void *arg)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = arg;
|
|
|
|
|
|
|
|
if (sc->sc_watchdog_timer != 0 && --sc->sc_watchdog_timer == 0) {
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
device_printf(sc->sc_dev, "device timeout\n");
|
|
|
|
counter_u64_add(sc->sc_ic.ic_oerrors, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
callout_schedule(&sc->sc_watchdog_ch, hz);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_attach_core(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
int error, have_bg = 0, have_a = 0;
|
2018-02-05 23:38:15 +00:00
|
|
|
uint16_t iost;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
KASSERT(bhnd_get_hwrev(sc->sc_dev) >= 5,
|
|
|
|
("unsupported revision %d", bhnd_get_hwrev(sc->sc_dev)));
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if ((error = bwn_core_forceclk(mac, true)))
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
if ((error = bhnd_read_iost(sc->sc_dev, &iost))) {
|
|
|
|
device_printf(sc->sc_dev, "error reading I/O status flags: "
|
|
|
|
"%d\n", error);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
have_a = (iost & BWN_IOST_HAVE_5GHZ) ? 1 : 0;
|
|
|
|
have_bg = (iost & BWN_IOST_HAVE_2GHZ) ? 1 : 0;
|
|
|
|
if (iost & BWN_IOST_DUALPHY) {
|
Introduce bwn(4) support for the bhnd(4) bus.
Currently, bwn(4) relies on the siba_bwn(4) bus driver to provide support
for the on-chip SSB interconnect found in Broadcom's older PCI(e) Wi-Fi
adapters. Non-PCI Wi-Fi adapters, as well as the newer BCMA interconnect
found in post-2009 Broadcom Wi-Fi hardware, are not supported by
siba_bwn(4).
The bhnd(4) bus driver (also used by the FreeBSD/MIPS Broadcom port)
provides a unified kernel interface to a superset of the hardware supported
by siba_bwn; by attaching bwn(4) via bhnd(4), we can support both modern
PCI(e) Wi-Fi devices based on the BCMA backplane interconnect, as well as
Broadcom MIPS WiSoCs that include a D11 MAC core directly attached to their
SSB or BCMA backplane.
This diff introduces opt-in bwn(4) support for bhnd(4) by providing:
- A small bwn(4) driver subclass, if_bwn_bhnd, that attaches via
bhnd(4) instead of siba_bwn(4).
- A bhndb(4)-based PCI host bridge driver, if_bwn_pci, that optionally
probes at a higher priority than the siba_bwn(4) PCI driver.
- A set of compatibility shims that perform translation of bwn(4)'s
siba_bwn function calls into their bhnd(9) API equivalents when bwn(4)
is attached via a bhnd(4) bus parent. When bwn(4) is attached via
siba_bwn(4), all siba_bwn function calls are simply passed through to
their original implementations.
To test bwn(4) with bhnd(4), place the following lines in loader.conf(5):
hw.bwn_pci.preferred="1"
if_bwn_pci_load="YES
bwn_v4_ucode_load="YES"
bwn_v4_lp_ucode_load="YES"
To verify that bwn(4) is using bhnd(4), you can check dmesg:
bwn0: <Broadcom 802.11 MAC/PHY/Radio, rev 15> ... on bhnd0
... or devinfo(8):
pcib2
pci2
bwn_pci0
bhndb0
bhnd0
bwn0
...
bwn(4)/bhnd(4) has been tested for regressions with most chipsets currently
supported by bwn(4), including:
- BCM4312
- BCM4318
- BCM4321
With minimal changes to the DMA code (not included in this commit), I was
also able to test support for newer BCMA devices by bringing up basic
working Wi-Fi on two previously unsupported, BCMA-based N-PHY chipsets:
- BCM43224
- BCM43225
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation & Plausible Labs
Differential Revision: https://reviews.freebsd.org/D13041
2017-12-02 02:21:27 +00:00
|
|
|
have_bg = 1;
|
|
|
|
have_a = 1;
|
|
|
|
}
|
2018-02-05 23:38:15 +00:00
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-06-05 07:55:21 +00:00
|
|
|
#if 0
|
2018-02-05 23:38:15 +00:00
|
|
|
device_printf(sc->sc_dev, "%s: iost=0x%04hx, have_a=%d, have_bg=%d,"
|
Introduce bwn(4) support for the bhnd(4) bus.
Currently, bwn(4) relies on the siba_bwn(4) bus driver to provide support
for the on-chip SSB interconnect found in Broadcom's older PCI(e) Wi-Fi
adapters. Non-PCI Wi-Fi adapters, as well as the newer BCMA interconnect
found in post-2009 Broadcom Wi-Fi hardware, are not supported by
siba_bwn(4).
The bhnd(4) bus driver (also used by the FreeBSD/MIPS Broadcom port)
provides a unified kernel interface to a superset of the hardware supported
by siba_bwn; by attaching bwn(4) via bhnd(4), we can support both modern
PCI(e) Wi-Fi devices based on the BCMA backplane interconnect, as well as
Broadcom MIPS WiSoCs that include a D11 MAC core directly attached to their
SSB or BCMA backplane.
This diff introduces opt-in bwn(4) support for bhnd(4) by providing:
- A small bwn(4) driver subclass, if_bwn_bhnd, that attaches via
bhnd(4) instead of siba_bwn(4).
- A bhndb(4)-based PCI host bridge driver, if_bwn_pci, that optionally
probes at a higher priority than the siba_bwn(4) PCI driver.
- A set of compatibility shims that perform translation of bwn(4)'s
siba_bwn function calls into their bhnd(9) API equivalents when bwn(4)
is attached via a bhnd(4) bus parent. When bwn(4) is attached via
siba_bwn(4), all siba_bwn function calls are simply passed through to
their original implementations.
To test bwn(4) with bhnd(4), place the following lines in loader.conf(5):
hw.bwn_pci.preferred="1"
if_bwn_pci_load="YES
bwn_v4_ucode_load="YES"
bwn_v4_lp_ucode_load="YES"
To verify that bwn(4) is using bhnd(4), you can check dmesg:
bwn0: <Broadcom 802.11 MAC/PHY/Radio, rev 15> ... on bhnd0
... or devinfo(8):
pcib2
pci2
bwn_pci0
bhndb0
bhnd0
bwn0
...
bwn(4)/bhnd(4) has been tested for regressions with most chipsets currently
supported by bwn(4), including:
- BCM4312
- BCM4318
- BCM4321
With minimal changes to the DMA code (not included in this commit), I was
also able to test support for newer BCMA devices by bringing up basic
working Wi-Fi on two previously unsupported, BCMA-based N-PHY chipsets:
- BCM43224
- BCM43225
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation & Plausible Labs
Differential Revision: https://reviews.freebsd.org/D13041
2017-12-02 02:21:27 +00:00
|
|
|
" deviceid=0x%04x, siba_deviceid=0x%04x\n",
|
|
|
|
__func__,
|
2018-02-05 23:38:15 +00:00
|
|
|
iost,
|
Introduce bwn(4) support for the bhnd(4) bus.
Currently, bwn(4) relies on the siba_bwn(4) bus driver to provide support
for the on-chip SSB interconnect found in Broadcom's older PCI(e) Wi-Fi
adapters. Non-PCI Wi-Fi adapters, as well as the newer BCMA interconnect
found in post-2009 Broadcom Wi-Fi hardware, are not supported by
siba_bwn(4).
The bhnd(4) bus driver (also used by the FreeBSD/MIPS Broadcom port)
provides a unified kernel interface to a superset of the hardware supported
by siba_bwn; by attaching bwn(4) via bhnd(4), we can support both modern
PCI(e) Wi-Fi devices based on the BCMA backplane interconnect, as well as
Broadcom MIPS WiSoCs that include a D11 MAC core directly attached to their
SSB or BCMA backplane.
This diff introduces opt-in bwn(4) support for bhnd(4) by providing:
- A small bwn(4) driver subclass, if_bwn_bhnd, that attaches via
bhnd(4) instead of siba_bwn(4).
- A bhndb(4)-based PCI host bridge driver, if_bwn_pci, that optionally
probes at a higher priority than the siba_bwn(4) PCI driver.
- A set of compatibility shims that perform translation of bwn(4)'s
siba_bwn function calls into their bhnd(9) API equivalents when bwn(4)
is attached via a bhnd(4) bus parent. When bwn(4) is attached via
siba_bwn(4), all siba_bwn function calls are simply passed through to
their original implementations.
To test bwn(4) with bhnd(4), place the following lines in loader.conf(5):
hw.bwn_pci.preferred="1"
if_bwn_pci_load="YES
bwn_v4_ucode_load="YES"
bwn_v4_lp_ucode_load="YES"
To verify that bwn(4) is using bhnd(4), you can check dmesg:
bwn0: <Broadcom 802.11 MAC/PHY/Radio, rev 15> ... on bhnd0
... or devinfo(8):
pcib2
pci2
bwn_pci0
bhndb0
bhnd0
bwn0
...
bwn(4)/bhnd(4) has been tested for regressions with most chipsets currently
supported by bwn(4), including:
- BCM4312
- BCM4318
- BCM4321
With minimal changes to the DMA code (not included in this commit), I was
also able to test support for newer BCMA devices by bringing up basic
working Wi-Fi on two previously unsupported, BCMA-based N-PHY chipsets:
- BCM43224
- BCM43225
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation & Plausible Labs
Differential Revision: https://reviews.freebsd.org/D13041
2017-12-02 02:21:27 +00:00
|
|
|
have_a,
|
|
|
|
have_bg,
|
2018-02-05 23:38:15 +00:00
|
|
|
sc->sc_board_info.board_devid,
|
|
|
|
sc->sc_cid.chip_id);
|
2016-06-05 07:55:21 +00:00
|
|
|
#endif
|
2016-05-16 20:26:30 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Guess at whether it has A-PHY or G-PHY.
|
|
|
|
* This is just used for resetting the core to probe things;
|
|
|
|
* we will re-guess once it's all up and working.
|
|
|
|
*/
|
2018-02-05 23:38:15 +00:00
|
|
|
error = bwn_reset_core(mac, have_bg);
|
|
|
|
if (error)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine the DMA engine type
|
|
|
|
*/
|
|
|
|
if (iost & BHND_IOST_DMA64) {
|
|
|
|
mac->mac_dmatype = BHND_DMA_ADDR_64BIT;
|
|
|
|
} else {
|
|
|
|
uint32_t tmp;
|
|
|
|
uint16_t base;
|
|
|
|
|
|
|
|
base = bwn_dma_base(0, 0);
|
|
|
|
BWN_WRITE_4(mac, base + BWN_DMA32_TXCTL,
|
|
|
|
BWN_DMA32_TXADDREXT_MASK);
|
|
|
|
tmp = BWN_READ_4(mac, base + BWN_DMA32_TXCTL);
|
|
|
|
if (tmp & BWN_DMA32_TXADDREXT_MASK) {
|
|
|
|
mac->mac_dmatype = BHND_DMA_ADDR_32BIT;
|
|
|
|
} else {
|
|
|
|
mac->mac_dmatype = BHND_DMA_ADDR_30BIT;
|
|
|
|
}
|
|
|
|
}
|
2016-05-16 20:26:30 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the PHY version.
|
|
|
|
*/
|
2016-05-24 04:58:58 +00:00
|
|
|
error = bwn_phy_getinfo(mac, have_bg);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (error)
|
|
|
|
goto fail;
|
|
|
|
|
2016-06-05 07:55:21 +00:00
|
|
|
/*
|
|
|
|
* This is the whitelist of devices which we "believe"
|
|
|
|
* the SPROM PHY config from. The rest are "guessed".
|
|
|
|
*/
|
2018-02-05 23:38:15 +00:00
|
|
|
if (sc->sc_board_info.board_devid != PCI_DEVID_BCM4311_D11DUAL &&
|
|
|
|
sc->sc_board_info.board_devid != PCI_DEVID_BCM4328_D11G &&
|
|
|
|
sc->sc_board_info.board_devid != PCI_DEVID_BCM4318_D11DUAL &&
|
|
|
|
sc->sc_board_info.board_devid != PCI_DEVID_BCM4306_D11DUAL &&
|
|
|
|
sc->sc_board_info.board_devid != PCI_DEVID_BCM4321_D11N &&
|
|
|
|
sc->sc_board_info.board_devid != PCI_DEVID_BCM4322_D11N) {
|
2010-02-16 01:44:14 +00:00
|
|
|
have_a = have_bg = 0;
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_A)
|
|
|
|
have_a = 1;
|
|
|
|
else if (mac->mac_phy.type == BWN_PHYTYPE_G ||
|
|
|
|
mac->mac_phy.type == BWN_PHYTYPE_N ||
|
|
|
|
mac->mac_phy.type == BWN_PHYTYPE_LP)
|
|
|
|
have_bg = 1;
|
|
|
|
else
|
|
|
|
KASSERT(0 == 1, ("%s: unknown phy type (%d)", __func__,
|
|
|
|
mac->mac_phy.type));
|
|
|
|
}
|
2016-05-16 20:26:30 +00:00
|
|
|
|
|
|
|
/*
|
2016-05-18 05:56:25 +00:00
|
|
|
* XXX The PHY-G support doesn't do 5GHz operation.
|
2016-05-16 20:26:30 +00:00
|
|
|
*/
|
2010-02-16 01:44:14 +00:00
|
|
|
if (mac->mac_phy.type != BWN_PHYTYPE_LP &&
|
|
|
|
mac->mac_phy.type != BWN_PHYTYPE_N) {
|
2016-05-16 20:26:30 +00:00
|
|
|
device_printf(sc->sc_dev,
|
2016-05-18 05:56:25 +00:00
|
|
|
"%s: forcing 2GHz only; no dual-band support for PHY\n",
|
2016-05-16 20:26:30 +00:00
|
|
|
__func__);
|
2010-02-16 01:44:14 +00:00
|
|
|
have_a = 0;
|
|
|
|
have_bg = 1;
|
|
|
|
}
|
|
|
|
|
2016-05-17 07:12:00 +00:00
|
|
|
mac->mac_phy.phy_n = NULL;
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_G) {
|
|
|
|
mac->mac_phy.attach = bwn_phy_g_attach;
|
|
|
|
mac->mac_phy.detach = bwn_phy_g_detach;
|
|
|
|
mac->mac_phy.prepare_hw = bwn_phy_g_prepare_hw;
|
|
|
|
mac->mac_phy.init_pre = bwn_phy_g_init_pre;
|
|
|
|
mac->mac_phy.init = bwn_phy_g_init;
|
|
|
|
mac->mac_phy.exit = bwn_phy_g_exit;
|
|
|
|
mac->mac_phy.phy_read = bwn_phy_g_read;
|
|
|
|
mac->mac_phy.phy_write = bwn_phy_g_write;
|
|
|
|
mac->mac_phy.rf_read = bwn_phy_g_rf_read;
|
|
|
|
mac->mac_phy.rf_write = bwn_phy_g_rf_write;
|
|
|
|
mac->mac_phy.use_hwpctl = bwn_phy_g_hwpctl;
|
|
|
|
mac->mac_phy.rf_onoff = bwn_phy_g_rf_onoff;
|
|
|
|
mac->mac_phy.switch_analog = bwn_phy_switch_analog;
|
|
|
|
mac->mac_phy.switch_channel = bwn_phy_g_switch_channel;
|
|
|
|
mac->mac_phy.get_default_chan = bwn_phy_g_get_default_chan;
|
|
|
|
mac->mac_phy.set_antenna = bwn_phy_g_set_antenna;
|
|
|
|
mac->mac_phy.set_im = bwn_phy_g_im;
|
|
|
|
mac->mac_phy.recalc_txpwr = bwn_phy_g_recalc_txpwr;
|
|
|
|
mac->mac_phy.set_txpwr = bwn_phy_g_set_txpwr;
|
|
|
|
mac->mac_phy.task_15s = bwn_phy_g_task_15s;
|
|
|
|
mac->mac_phy.task_60s = bwn_phy_g_task_60s;
|
|
|
|
} else if (mac->mac_phy.type == BWN_PHYTYPE_LP) {
|
|
|
|
mac->mac_phy.init_pre = bwn_phy_lp_init_pre;
|
|
|
|
mac->mac_phy.init = bwn_phy_lp_init;
|
|
|
|
mac->mac_phy.phy_read = bwn_phy_lp_read;
|
|
|
|
mac->mac_phy.phy_write = bwn_phy_lp_write;
|
|
|
|
mac->mac_phy.phy_maskset = bwn_phy_lp_maskset;
|
|
|
|
mac->mac_phy.rf_read = bwn_phy_lp_rf_read;
|
|
|
|
mac->mac_phy.rf_write = bwn_phy_lp_rf_write;
|
|
|
|
mac->mac_phy.rf_onoff = bwn_phy_lp_rf_onoff;
|
|
|
|
mac->mac_phy.switch_analog = bwn_phy_lp_switch_analog;
|
|
|
|
mac->mac_phy.switch_channel = bwn_phy_lp_switch_channel;
|
|
|
|
mac->mac_phy.get_default_chan = bwn_phy_lp_get_default_chan;
|
|
|
|
mac->mac_phy.set_antenna = bwn_phy_lp_set_antenna;
|
|
|
|
mac->mac_phy.task_60s = bwn_phy_lp_task_60s;
|
2016-05-17 07:12:00 +00:00
|
|
|
} else if (mac->mac_phy.type == BWN_PHYTYPE_N) {
|
|
|
|
mac->mac_phy.attach = bwn_phy_n_attach;
|
|
|
|
mac->mac_phy.detach = bwn_phy_n_detach;
|
|
|
|
mac->mac_phy.prepare_hw = bwn_phy_n_prepare_hw;
|
|
|
|
mac->mac_phy.init_pre = bwn_phy_n_init_pre;
|
|
|
|
mac->mac_phy.init = bwn_phy_n_init;
|
|
|
|
mac->mac_phy.exit = bwn_phy_n_exit;
|
|
|
|
mac->mac_phy.phy_read = bwn_phy_n_read;
|
|
|
|
mac->mac_phy.phy_write = bwn_phy_n_write;
|
|
|
|
mac->mac_phy.rf_read = bwn_phy_n_rf_read;
|
|
|
|
mac->mac_phy.rf_write = bwn_phy_n_rf_write;
|
|
|
|
mac->mac_phy.use_hwpctl = bwn_phy_n_hwpctl;
|
|
|
|
mac->mac_phy.rf_onoff = bwn_phy_n_rf_onoff;
|
|
|
|
mac->mac_phy.switch_analog = bwn_phy_n_switch_analog;
|
|
|
|
mac->mac_phy.switch_channel = bwn_phy_n_switch_channel;
|
|
|
|
mac->mac_phy.get_default_chan = bwn_phy_n_get_default_chan;
|
|
|
|
mac->mac_phy.set_antenna = bwn_phy_n_set_antenna;
|
|
|
|
mac->mac_phy.set_im = bwn_phy_n_im;
|
|
|
|
mac->mac_phy.recalc_txpwr = bwn_phy_n_recalc_txpwr;
|
|
|
|
mac->mac_phy.set_txpwr = bwn_phy_n_set_txpwr;
|
|
|
|
mac->mac_phy.task_15s = bwn_phy_n_task_15s;
|
|
|
|
mac->mac_phy.task_60s = bwn_phy_n_task_60s;
|
2010-02-16 01:44:14 +00:00
|
|
|
} else {
|
|
|
|
device_printf(sc->sc_dev, "unsupported PHY type (%d)\n",
|
|
|
|
mac->mac_phy.type);
|
|
|
|
error = ENXIO;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
mac->mac_phy.gmode = have_bg;
|
|
|
|
if (mac->mac_phy.attach != NULL) {
|
|
|
|
error = mac->mac_phy.attach(mac);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "failed\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
error = bwn_reset_core(mac, have_bg);
|
|
|
|
if (error)
|
|
|
|
goto fail;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
error = bwn_chiptest(mac);
|
|
|
|
if (error)
|
|
|
|
goto fail;
|
|
|
|
error = bwn_setup_channels(mac, have_bg, have_a);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "failed to setup channels\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_curmac == NULL)
|
|
|
|
sc->sc_curmac = mac;
|
|
|
|
|
|
|
|
error = bwn_dma_attach(mac);
|
|
|
|
if (error != 0) {
|
|
|
|
device_printf(sc->sc_dev, "failed to initialize DMA\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
mac->mac_phy.switch_analog(mac, 0);
|
|
|
|
|
|
|
|
fail:
|
2018-02-05 23:38:15 +00:00
|
|
|
bhnd_suspend_hw(sc->sc_dev, 0);
|
2016-06-05 07:56:28 +00:00
|
|
|
bwn_release_firmware(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2016-05-14 23:20:46 +00:00
|
|
|
/*
|
2018-02-05 23:38:15 +00:00
|
|
|
* Reset
|
2016-05-14 23:20:46 +00:00
|
|
|
*/
|
2018-02-05 23:38:15 +00:00
|
|
|
int
|
2016-05-14 23:20:46 +00:00
|
|
|
bwn_reset_core(struct bwn_mac *mac, int g_mode)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
struct bwn_softc *sc;
|
|
|
|
uint32_t ctl;
|
|
|
|
uint16_t ioctl, ioctl_mask;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
sc = mac->mac_sc;
|
2016-05-14 23:20:46 +00:00
|
|
|
|
|
|
|
DPRINTF(sc, BWN_DEBUG_RESET, "%s: g_mode=%d\n", __func__, g_mode);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
/* Reset core */
|
|
|
|
ioctl = (BWN_IOCTL_PHYCLOCK_ENABLE | BWN_IOCTL_PHYRESET);
|
2016-05-14 23:20:46 +00:00
|
|
|
if (g_mode)
|
2018-02-05 23:38:15 +00:00
|
|
|
ioctl |= BWN_IOCTL_SUPPORT_G;
|
2016-05-14 23:20:46 +00:00
|
|
|
|
|
|
|
/* XXX N-PHY only; and hard-code to 20MHz for now */
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_N)
|
2018-02-05 23:38:15 +00:00
|
|
|
ioctl |= BWN_IOCTL_PHY_BANDWIDTH_20MHZ;
|
|
|
|
|
|
|
|
if ((error = bhnd_reset_hw(sc->sc_dev, ioctl, ioctl))) {
|
|
|
|
device_printf(sc->sc_dev, "core reset failed: %d", error);
|
|
|
|
return (error);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
DELAY(2000);
|
|
|
|
|
2016-05-14 23:20:46 +00:00
|
|
|
/* Take PHY out of reset */
|
2018-02-05 23:38:15 +00:00
|
|
|
ioctl = BHND_IOCTL_CLK_FORCE;
|
|
|
|
ioctl_mask = BHND_IOCTL_CLK_FORCE |
|
|
|
|
BWN_IOCTL_PHYRESET |
|
|
|
|
BWN_IOCTL_PHYCLOCK_ENABLE;
|
|
|
|
|
|
|
|
if ((error = bhnd_write_ioctl(sc->sc_dev, ioctl, ioctl_mask))) {
|
|
|
|
device_printf(sc->sc_dev, "failed to set core ioctl flags: "
|
|
|
|
"%d\n", error);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2016-05-19 04:28:49 +00:00
|
|
|
DELAY(2000);
|
2018-02-05 23:38:15 +00:00
|
|
|
|
|
|
|
ioctl = BWN_IOCTL_PHYCLOCK_ENABLE;
|
|
|
|
if ((error = bhnd_write_ioctl(sc->sc_dev, ioctl, ioctl_mask))) {
|
|
|
|
device_printf(sc->sc_dev, "failed to set core ioctl flags: "
|
|
|
|
"%d\n", error);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2016-05-19 04:28:49 +00:00
|
|
|
DELAY(2000);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
if (mac->mac_phy.switch_analog != NULL)
|
|
|
|
mac->mac_phy.switch_analog(mac, 1);
|
|
|
|
|
|
|
|
ctl = BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_GMODE;
|
2016-05-14 23:20:46 +00:00
|
|
|
if (g_mode)
|
2010-02-16 01:44:14 +00:00
|
|
|
ctl |= BWN_MACCTL_GMODE;
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL, ctl | BWN_MACCTL_IHR_ON);
|
2018-02-05 23:38:15 +00:00
|
|
|
|
|
|
|
return (0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2016-05-24 04:58:58 +00:00
|
|
|
bwn_phy_getinfo(struct bwn_mac *mac, int gmode)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
struct bwn_phy *phy = &mac->mac_phy;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint32_t tmp;
|
|
|
|
|
|
|
|
/* PHY */
|
|
|
|
tmp = BWN_READ_2(mac, BWN_PHYVER);
|
2016-05-24 04:58:58 +00:00
|
|
|
phy->gmode = gmode;
|
2010-02-16 01:44:14 +00:00
|
|
|
phy->rf_on = 1;
|
|
|
|
phy->analog = (tmp & BWN_PHYVER_ANALOG) >> 12;
|
|
|
|
phy->type = (tmp & BWN_PHYVER_TYPE) >> 8;
|
|
|
|
phy->rev = (tmp & BWN_PHYVER_VERSION);
|
|
|
|
if ((phy->type == BWN_PHYTYPE_A && phy->rev >= 4) ||
|
|
|
|
(phy->type == BWN_PHYTYPE_B && phy->rev != 2 &&
|
|
|
|
phy->rev != 4 && phy->rev != 6 && phy->rev != 7) ||
|
|
|
|
(phy->type == BWN_PHYTYPE_G && phy->rev > 9) ||
|
2017-12-14 06:45:04 +00:00
|
|
|
(phy->type == BWN_PHYTYPE_N && phy->rev > 6) ||
|
2010-02-16 01:44:14 +00:00
|
|
|
(phy->type == BWN_PHYTYPE_LP && phy->rev > 2))
|
|
|
|
goto unsupphy;
|
|
|
|
|
|
|
|
/* RADIO */
|
2018-02-05 23:38:15 +00:00
|
|
|
BWN_WRITE_2(mac, BWN_RFCTL, BWN_RFCTL_ID);
|
|
|
|
tmp = BWN_READ_2(mac, BWN_RFDATALO);
|
|
|
|
BWN_WRITE_2(mac, BWN_RFCTL, BWN_RFCTL_ID);
|
|
|
|
tmp |= (uint32_t)BWN_READ_2(mac, BWN_RFDATAHI) << 16;
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
phy->rf_rev = (tmp & 0xf0000000) >> 28;
|
|
|
|
phy->rf_ver = (tmp & 0x0ffff000) >> 12;
|
|
|
|
phy->rf_manuf = (tmp & 0x00000fff);
|
2016-05-14 23:41:28 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For now, just always do full init (ie, what bwn has traditionally
|
|
|
|
* done)
|
|
|
|
*/
|
|
|
|
phy->phy_do_full_init = 1;
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
if (phy->rf_manuf != 0x17f) /* 0x17f is broadcom */
|
|
|
|
goto unsupradio;
|
|
|
|
if ((phy->type == BWN_PHYTYPE_A && (phy->rf_ver != 0x2060 ||
|
|
|
|
phy->rf_rev != 1 || phy->rf_manuf != 0x17f)) ||
|
|
|
|
(phy->type == BWN_PHYTYPE_B && (phy->rf_ver & 0xfff0) != 0x2050) ||
|
|
|
|
(phy->type == BWN_PHYTYPE_G && phy->rf_ver != 0x2050) ||
|
|
|
|
(phy->type == BWN_PHYTYPE_N &&
|
|
|
|
phy->rf_ver != 0x2055 && phy->rf_ver != 0x2056) ||
|
|
|
|
(phy->type == BWN_PHYTYPE_LP &&
|
|
|
|
phy->rf_ver != 0x2062 && phy->rf_ver != 0x2063))
|
|
|
|
goto unsupradio;
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
unsupphy:
|
|
|
|
device_printf(sc->sc_dev, "unsupported PHY (type %#x, rev %#x, "
|
|
|
|
"analog %#x)\n",
|
|
|
|
phy->type, phy->rev, phy->analog);
|
|
|
|
return (ENXIO);
|
|
|
|
unsupradio:
|
|
|
|
device_printf(sc->sc_dev, "unsupported radio (manuf %#x, ver %#x, "
|
|
|
|
"rev %#x)\n",
|
|
|
|
phy->rf_manuf, phy->rf_ver, phy->rf_rev);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_chiptest(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
#define TESTVAL0 0x55aaaa55
|
|
|
|
#define TESTVAL1 0xaa5555aa
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint32_t v, backup;
|
|
|
|
|
|
|
|
BWN_LOCK(sc);
|
|
|
|
|
|
|
|
backup = bwn_shm_read_4(mac, BWN_SHARED, 0);
|
|
|
|
|
|
|
|
bwn_shm_write_4(mac, BWN_SHARED, 0, TESTVAL0);
|
|
|
|
if (bwn_shm_read_4(mac, BWN_SHARED, 0) != TESTVAL0)
|
|
|
|
goto error;
|
|
|
|
bwn_shm_write_4(mac, BWN_SHARED, 0, TESTVAL1);
|
|
|
|
if (bwn_shm_read_4(mac, BWN_SHARED, 0) != TESTVAL1)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
bwn_shm_write_4(mac, BWN_SHARED, 0, backup);
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if ((bhnd_get_hwrev(sc->sc_dev) >= 3) &&
|
|
|
|
(bhnd_get_hwrev(sc->sc_dev) <= 10)) {
|
2010-02-16 01:44:14 +00:00
|
|
|
BWN_WRITE_2(mac, BWN_TSF_CFP_START, 0xaaaa);
|
|
|
|
BWN_WRITE_4(mac, BWN_TSF_CFP_START, 0xccccbbbb);
|
|
|
|
if (BWN_READ_2(mac, BWN_TSF_CFP_START_LOW) != 0xbbbb)
|
|
|
|
goto error;
|
|
|
|
if (BWN_READ_2(mac, BWN_TSF_CFP_START_HIGH) != 0xcccc)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
BWN_WRITE_4(mac, BWN_TSF_CFP_START, 0);
|
|
|
|
|
|
|
|
v = BWN_READ_4(mac, BWN_MACCTL) | BWN_MACCTL_GMODE;
|
|
|
|
if (v != (BWN_MACCTL_GMODE | BWN_MACCTL_IHR_ON))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
return (0);
|
|
|
|
error:
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
device_printf(sc->sc_dev, "failed to validate the chipaccess\n");
|
|
|
|
return (ENODEV);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_setup_channels(struct bwn_mac *mac, int have_bg, int have_a)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2016-05-20 08:58:06 +00:00
|
|
|
uint8_t bands[IEEE80211_MODE_BYTES];
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
memset(ic->ic_channels, 0, sizeof(ic->ic_channels));
|
|
|
|
ic->ic_nchans = 0;
|
|
|
|
|
2016-05-14 23:41:28 +00:00
|
|
|
DPRINTF(sc, BWN_DEBUG_EEPROM, "%s: called; bg=%d, a=%d\n",
|
|
|
|
__func__,
|
|
|
|
have_bg,
|
|
|
|
have_a);
|
|
|
|
|
2016-05-16 22:39:17 +00:00
|
|
|
if (have_bg) {
|
|
|
|
memset(bands, 0, sizeof(bands));
|
|
|
|
setbit(bands, IEEE80211_MODE_11B);
|
|
|
|
setbit(bands, IEEE80211_MODE_11G);
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_addchannels(ic->ic_channels, IEEE80211_CHAN_MAX,
|
2016-05-16 22:39:17 +00:00
|
|
|
&ic->ic_nchans, &bwn_chantable_bg, bands);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-16 22:39:17 +00:00
|
|
|
|
|
|
|
if (have_a) {
|
|
|
|
memset(bands, 0, sizeof(bands));
|
|
|
|
setbit(bands, IEEE80211_MODE_11A);
|
2016-05-14 23:50:15 +00:00
|
|
|
bwn_addchannels(ic->ic_channels, IEEE80211_CHAN_MAX,
|
2016-05-16 22:39:17 +00:00
|
|
|
&ic->ic_nchans, &bwn_chantable_a, bands);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
mac->mac_phy.supports_2ghz = have_bg;
|
|
|
|
mac->mac_phy.supports_5ghz = have_a;
|
|
|
|
|
|
|
|
return (ic->ic_nchans == 0 ? ENXIO : 0);
|
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
uint32_t
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_shm_read_4(struct bwn_mac *mac, uint16_t way, uint16_t offset)
|
|
|
|
{
|
|
|
|
uint32_t ret;
|
|
|
|
|
2010-02-23 05:02:10 +00:00
|
|
|
BWN_ASSERT_LOCKED(mac->mac_sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
if (way == BWN_SHARED) {
|
|
|
|
KASSERT((offset & 0x0001) == 0,
|
|
|
|
("%s:%d warn", __func__, __LINE__));
|
|
|
|
if (offset & 0x0003) {
|
|
|
|
bwn_shm_ctlword(mac, way, offset >> 2);
|
|
|
|
ret = BWN_READ_2(mac, BWN_SHM_DATA_UNALIGNED);
|
|
|
|
ret <<= 16;
|
|
|
|
bwn_shm_ctlword(mac, way, (offset >> 2) + 1);
|
|
|
|
ret |= BWN_READ_2(mac, BWN_SHM_DATA);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
offset >>= 2;
|
|
|
|
}
|
|
|
|
bwn_shm_ctlword(mac, way, offset);
|
|
|
|
ret = BWN_READ_4(mac, BWN_SHM_DATA);
|
|
|
|
out:
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
uint16_t
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_shm_read_2(struct bwn_mac *mac, uint16_t way, uint16_t offset)
|
|
|
|
{
|
|
|
|
uint16_t ret;
|
|
|
|
|
2010-02-23 05:02:10 +00:00
|
|
|
BWN_ASSERT_LOCKED(mac->mac_sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
if (way == BWN_SHARED) {
|
|
|
|
KASSERT((offset & 0x0001) == 0,
|
|
|
|
("%s:%d warn", __func__, __LINE__));
|
|
|
|
if (offset & 0x0003) {
|
|
|
|
bwn_shm_ctlword(mac, way, offset >> 2);
|
|
|
|
ret = BWN_READ_2(mac, BWN_SHM_DATA_UNALIGNED);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
offset >>= 2;
|
|
|
|
}
|
|
|
|
bwn_shm_ctlword(mac, way, offset);
|
|
|
|
ret = BWN_READ_2(mac, BWN_SHM_DATA);
|
|
|
|
out:
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_shm_ctlword(struct bwn_mac *mac, uint16_t way,
|
|
|
|
uint16_t offset)
|
|
|
|
{
|
|
|
|
uint32_t control;
|
|
|
|
|
|
|
|
control = way;
|
|
|
|
control <<= 16;
|
|
|
|
control |= offset;
|
|
|
|
BWN_WRITE_4(mac, BWN_SHM_CONTROL, control);
|
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
void
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_shm_write_4(struct bwn_mac *mac, uint16_t way, uint16_t offset,
|
|
|
|
uint32_t value)
|
|
|
|
{
|
2010-02-23 05:02:10 +00:00
|
|
|
BWN_ASSERT_LOCKED(mac->mac_sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
if (way == BWN_SHARED) {
|
|
|
|
KASSERT((offset & 0x0001) == 0,
|
|
|
|
("%s:%d warn", __func__, __LINE__));
|
|
|
|
if (offset & 0x0003) {
|
|
|
|
bwn_shm_ctlword(mac, way, offset >> 2);
|
|
|
|
BWN_WRITE_2(mac, BWN_SHM_DATA_UNALIGNED,
|
|
|
|
(value >> 16) & 0xffff);
|
|
|
|
bwn_shm_ctlword(mac, way, (offset >> 2) + 1);
|
|
|
|
BWN_WRITE_2(mac, BWN_SHM_DATA, value & 0xffff);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
offset >>= 2;
|
|
|
|
}
|
|
|
|
bwn_shm_ctlword(mac, way, offset);
|
|
|
|
BWN_WRITE_4(mac, BWN_SHM_DATA, value);
|
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
void
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_shm_write_2(struct bwn_mac *mac, uint16_t way, uint16_t offset,
|
|
|
|
uint16_t value)
|
|
|
|
{
|
2010-02-23 05:02:10 +00:00
|
|
|
BWN_ASSERT_LOCKED(mac->mac_sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
if (way == BWN_SHARED) {
|
|
|
|
KASSERT((offset & 0x0001) == 0,
|
|
|
|
("%s:%d warn", __func__, __LINE__));
|
|
|
|
if (offset & 0x0003) {
|
|
|
|
bwn_shm_ctlword(mac, way, offset >> 2);
|
|
|
|
BWN_WRITE_2(mac, BWN_SHM_DATA_UNALIGNED, value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
offset >>= 2;
|
|
|
|
}
|
|
|
|
bwn_shm_ctlword(mac, way, offset);
|
|
|
|
BWN_WRITE_2(mac, BWN_SHM_DATA, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_addchannels(struct ieee80211_channel chans[], int maxchans, int *nchans,
|
2016-05-16 22:39:17 +00:00
|
|
|
const struct bwn_channelinfo *ci, const uint8_t bands[])
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-16 22:39:17 +00:00
|
|
|
int i, error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-16 22:39:17 +00:00
|
|
|
for (i = 0, error = 0; i < ci->nchannels && error == 0; i++) {
|
|
|
|
const struct bwn_channel *hc = &ci->channels[i];
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-16 22:39:17 +00:00
|
|
|
error = ieee80211_add_channel(chans, maxchans, nchans,
|
|
|
|
hc->ieee, hc->freq, hc->maxTxPow, 0, bands);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
|
|
|
|
const struct ieee80211_bpf_params *params)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct ieee80211com *ic = ni->ni_ic;
|
|
|
|
struct bwn_softc *sc = ic->ic_softc;
|
|
|
|
struct bwn_mac *mac = sc->sc_curmac;
|
|
|
|
int error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if ((sc->sc_flags & BWN_FLAG_RUNNING) == 0 ||
|
|
|
|
mac->mac_status < BWN_MAC_STATUS_STARTED) {
|
|
|
|
m_freem(m);
|
|
|
|
return (ENETDOWN);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_LOCK(sc);
|
|
|
|
if (bwn_tx_isfull(sc, m)) {
|
|
|
|
m_freem(m);
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
return (ENOBUFS);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
error = bwn_tx_start(sc, ni, m);
|
|
|
|
if (error == 0)
|
|
|
|
sc->sc_watchdog_timer = 5;
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
return (error);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/*
|
|
|
|
* Callback from the 802.11 layer to update the slot time
|
|
|
|
* based on the current setting. We use it to notify the
|
|
|
|
* firmware of ERP changes and the f/w takes care of things
|
|
|
|
* like slot time and preamble.
|
|
|
|
*/
|
2010-02-16 01:44:14 +00:00
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_updateslot(struct ieee80211com *ic)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = ic->ic_softc;
|
|
|
|
struct bwn_mac *mac;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_LOCK(sc);
|
|
|
|
if (sc->sc_flags & BWN_FLAG_RUNNING) {
|
|
|
|
mac = (struct bwn_mac *)sc->sc_curmac;
|
|
|
|
bwn_set_slot_time(mac, IEEE80211_GET_SLOTTIME(ic));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_UNLOCK(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/*
|
|
|
|
* Callback from the 802.11 layer after a promiscuous mode change.
|
|
|
|
* Note this interface does not check the operating mode as this
|
|
|
|
* is an internal callback and we are expected to honor the current
|
|
|
|
* state (e.g. this is used for setting the interface in promiscuous
|
|
|
|
* mode when operating in hostap mode to do ACS).
|
|
|
|
*/
|
2010-02-16 01:44:14 +00:00
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_update_promisc(struct ieee80211com *ic)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = ic->ic_softc;
|
|
|
|
struct bwn_mac *mac = sc->sc_curmac;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_LOCK(sc);
|
|
|
|
mac = sc->sc_curmac;
|
|
|
|
if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) {
|
|
|
|
if (ic->ic_promisc > 0)
|
|
|
|
sc->sc_filters |= BWN_MACCTL_PROMISC;
|
|
|
|
else
|
|
|
|
sc->sc_filters &= ~BWN_MACCTL_PROMISC;
|
|
|
|
bwn_set_opmode(mac);
|
|
|
|
}
|
|
|
|
BWN_UNLOCK(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/*
|
|
|
|
* Callback from the 802.11 layer to update WME parameters.
|
|
|
|
*/
|
2010-02-16 01:44:14 +00:00
|
|
|
static int
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_wme_update(struct ieee80211com *ic)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = ic->ic_softc;
|
|
|
|
struct bwn_mac *mac = sc->sc_curmac;
|
2018-01-02 00:07:28 +00:00
|
|
|
struct chanAccParams chp;
|
2016-05-02 22:58:11 +00:00
|
|
|
struct wmeParams *wmep;
|
|
|
|
int i;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-01-02 00:07:28 +00:00
|
|
|
ieee80211_wme_ic_getparams(ic, &chp);
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_LOCK(sc);
|
|
|
|
mac = sc->sc_curmac;
|
|
|
|
if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) {
|
|
|
|
bwn_mac_suspend(mac);
|
|
|
|
for (i = 0; i < N(sc->sc_wmeParams); i++) {
|
2018-01-02 00:07:28 +00:00
|
|
|
wmep = &chp.cap_wmeParams[i];
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_wme_loadparams(mac, wmep, bwn_wme_shm_offsets[i]);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_mac_enable(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_UNLOCK(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_scan_start(struct ieee80211com *ic)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = ic->ic_softc;
|
|
|
|
struct bwn_mac *mac;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_LOCK(sc);
|
|
|
|
mac = sc->sc_curmac;
|
|
|
|
if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) {
|
|
|
|
sc->sc_filters |= BWN_MACCTL_BEACON_PROMISC;
|
|
|
|
bwn_set_opmode(mac);
|
|
|
|
/* disable CFP update during scan */
|
|
|
|
bwn_hf_write(mac, bwn_hf_read(mac) | BWN_HF_SKIP_CFP_UPDATE);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_UNLOCK(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_scan_end(struct ieee80211com *ic)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = ic->ic_softc;
|
|
|
|
struct bwn_mac *mac;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_LOCK(sc);
|
|
|
|
mac = sc->sc_curmac;
|
|
|
|
if (mac != NULL && mac->mac_status >= BWN_MAC_STATUS_INITED) {
|
|
|
|
sc->sc_filters &= ~BWN_MACCTL_BEACON_PROMISC;
|
|
|
|
bwn_set_opmode(mac);
|
|
|
|
bwn_hf_write(mac, bwn_hf_read(mac) & ~BWN_HF_SKIP_CFP_UPDATE);
|
|
|
|
}
|
|
|
|
BWN_UNLOCK(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_set_channel(struct ieee80211com *ic)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = ic->ic_softc;
|
|
|
|
struct bwn_mac *mac = sc->sc_curmac;
|
|
|
|
struct bwn_phy *phy = &mac->mac_phy;
|
|
|
|
int chan, error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_LOCK(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
error = bwn_switch_band(sc, ic->ic_curchan);
|
|
|
|
if (error)
|
|
|
|
goto fail;
|
|
|
|
bwn_mac_suspend(mac);
|
|
|
|
bwn_set_txretry(mac, BWN_RETRY_SHORT, BWN_RETRY_LONG);
|
|
|
|
chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
|
|
|
|
if (chan != phy->chan)
|
|
|
|
bwn_switch_channel(mac, chan);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/* TX power level */
|
|
|
|
if (ic->ic_curchan->ic_maxpower != 0 &&
|
|
|
|
ic->ic_curchan->ic_maxpower != phy->txpower) {
|
|
|
|
phy->txpower = ic->ic_curchan->ic_maxpower / 2;
|
|
|
|
bwn_phy_txpower_check(mac, BWN_TXPWR_IGNORE_TIME |
|
|
|
|
BWN_TXPWR_IGNORE_TSSI);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_set_txantenna(mac, BWN_ANT_DEFAULT);
|
|
|
|
if (phy->set_antenna)
|
|
|
|
phy->set_antenna(mac, BWN_ANT_DEFAULT);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (sc->sc_rf_enabled != phy->rf_on) {
|
|
|
|
if (sc->sc_rf_enabled) {
|
|
|
|
bwn_rf_turnon(mac);
|
|
|
|
if (!(mac->mac_flags & BWN_MAC_FLAG_RADIO_ON))
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"please turn on the RF switch\n");
|
|
|
|
} else
|
|
|
|
bwn_rf_turnoff(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_mac_enable(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
fail:
|
|
|
|
/*
|
|
|
|
* Setup radio tap channel freq and flags
|
|
|
|
*/
|
|
|
|
sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
|
|
|
|
htole16(ic->ic_curchan->ic_freq);
|
|
|
|
sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
|
|
|
|
htole16(ic->ic_curchan->ic_flags & 0xffff);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_UNLOCK(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static struct ieee80211vap *
|
|
|
|
bwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
|
|
|
|
enum ieee80211_opmode opmode, int flags,
|
|
|
|
const uint8_t bssid[IEEE80211_ADDR_LEN],
|
|
|
|
const uint8_t mac[IEEE80211_ADDR_LEN])
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct ieee80211vap *vap;
|
|
|
|
struct bwn_vap *bvp;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
switch (opmode) {
|
|
|
|
case IEEE80211_M_HOSTAP:
|
|
|
|
case IEEE80211_M_MBSS:
|
|
|
|
case IEEE80211_M_STA:
|
|
|
|
case IEEE80211_M_WDS:
|
|
|
|
case IEEE80211_M_MONITOR:
|
|
|
|
case IEEE80211_M_IBSS:
|
|
|
|
case IEEE80211_M_AHDEMO:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (NULL);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bvp = malloc(sizeof(struct bwn_vap), M_80211_VAP, M_WAITOK | M_ZERO);
|
|
|
|
vap = &bvp->bv_vap;
|
|
|
|
ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
|
|
|
|
/* override with driver methods */
|
|
|
|
bvp->bv_newstate = vap->iv_newstate;
|
|
|
|
vap->iv_newstate = bwn_newstate;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/* override max aid so sta's cannot assoc when we're out of sta id's */
|
|
|
|
vap->iv_max_aid = BWN_STAID_MAX;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
ieee80211_ratectl_init(vap);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/* complete setup */
|
|
|
|
ieee80211_vap_attach(vap, ieee80211_media_change,
|
|
|
|
ieee80211_media_status, mac);
|
|
|
|
return (vap);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_vap_delete(struct ieee80211vap *vap)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_vap *bvp = BWN_VAP(vap);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
ieee80211_ratectl_deinit(vap);
|
|
|
|
ieee80211_vap_detach(vap);
|
|
|
|
free(bvp, M_80211_VAP);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_init(struct bwn_softc *sc)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_mac *mac;
|
|
|
|
int error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
|
2016-05-14 23:42:37 +00:00
|
|
|
DPRINTF(sc, BWN_DEBUG_RESET, "%s: called\n", __func__);
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bzero(sc->sc_bssid, IEEE80211_ADDR_LEN);
|
|
|
|
sc->sc_flags |= BWN_FLAG_NEED_BEACON_TP;
|
|
|
|
sc->sc_filters = 0;
|
|
|
|
bwn_wme_clear(sc);
|
|
|
|
sc->sc_beacons[0] = sc->sc_beacons[1] = 0;
|
|
|
|
sc->sc_rf_enabled = 1;
|
|
|
|
|
|
|
|
mac = sc->sc_curmac;
|
|
|
|
if (mac->mac_status == BWN_MAC_STATUS_UNINIT) {
|
|
|
|
error = bwn_core_init(mac);
|
|
|
|
if (error != 0)
|
|
|
|
return (error);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
if (mac->mac_status == BWN_MAC_STATUS_INITED)
|
|
|
|
bwn_core_start(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_set_opmode(mac);
|
|
|
|
bwn_set_pretbtt(mac);
|
|
|
|
bwn_spu_setdelay(mac, 0);
|
|
|
|
bwn_set_macaddr(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
sc->sc_flags |= BWN_FLAG_RUNNING;
|
|
|
|
callout_reset(&sc->sc_rfswitch_ch, hz, bwn_rfswitch, sc);
|
|
|
|
callout_reset(&sc->sc_watchdog_ch, hz, bwn_watchdog, sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
return (0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_stop(struct bwn_softc *sc)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_mac *mac = sc->sc_curmac;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_ASSERT_LOCKED(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-14 23:42:37 +00:00
|
|
|
DPRINTF(sc, BWN_DEBUG_RESET, "%s: called\n", __func__);
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (mac->mac_status >= BWN_MAC_STATUS_INITED) {
|
|
|
|
/* XXX FIXME opmode not based on VAP */
|
|
|
|
bwn_set_opmode(mac);
|
|
|
|
bwn_set_macaddr(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (mac->mac_status >= BWN_MAC_STATUS_STARTED)
|
|
|
|
bwn_core_stop(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
callout_stop(&sc->sc_led_blink_ch);
|
|
|
|
sc->sc_led_blinking = 0;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_core_exit(mac);
|
|
|
|
sc->sc_rf_enabled = 0;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
sc->sc_flags &= ~BWN_FLAG_RUNNING;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_wme_clear(struct bwn_softc *sc)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
#define MS(_v, _f) (((_v) & _f) >> _f##_S)
|
|
|
|
struct wmeParams *p;
|
|
|
|
unsigned int i;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(N(bwn_wme_shm_offsets) == N(sc->sc_wmeParams),
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
for (i = 0; i < N(sc->sc_wmeParams); i++) {
|
|
|
|
p = &(sc->sc_wmeParams[i]);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
switch (bwn_wme_shm_offsets[i]) {
|
|
|
|
case BWN_WME_VOICE:
|
|
|
|
p->wmep_txopLimit = 0;
|
|
|
|
p->wmep_aifsn = 2;
|
|
|
|
/* XXX FIXME: log2(cwmin) */
|
|
|
|
p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN);
|
|
|
|
p->wmep_logcwmax = MS(0x0001, WME_PARAM_LOGCWMAX);
|
|
|
|
break;
|
|
|
|
case BWN_WME_VIDEO:
|
|
|
|
p->wmep_txopLimit = 0;
|
|
|
|
p->wmep_aifsn = 2;
|
|
|
|
/* XXX FIXME: log2(cwmin) */
|
|
|
|
p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN);
|
|
|
|
p->wmep_logcwmax = MS(0x0001, WME_PARAM_LOGCWMAX);
|
|
|
|
break;
|
|
|
|
case BWN_WME_BESTEFFORT:
|
|
|
|
p->wmep_txopLimit = 0;
|
|
|
|
p->wmep_aifsn = 3;
|
|
|
|
/* XXX FIXME: log2(cwmin) */
|
|
|
|
p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN);
|
|
|
|
p->wmep_logcwmax = MS(0x03ff, WME_PARAM_LOGCWMAX);
|
|
|
|
break;
|
|
|
|
case BWN_WME_BACKGROUND:
|
|
|
|
p->wmep_txopLimit = 0;
|
|
|
|
p->wmep_aifsn = 7;
|
|
|
|
/* XXX FIXME: log2(cwmin) */
|
|
|
|
p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN);
|
|
|
|
p->wmep_logcwmax = MS(0x03ff, WME_PARAM_LOGCWMAX);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
}
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
static int
|
|
|
|
bwn_core_forceclk(struct bwn_mac *mac, bool force)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc;
|
|
|
|
bhnd_clock clock;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
sc = mac->mac_sc;
|
|
|
|
|
|
|
|
/* On PMU equipped devices, we do not need to force the HT clock */
|
|
|
|
if (sc->sc_pmu != NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/* Issue a PMU clock request */
|
|
|
|
if (force)
|
|
|
|
clock = BHND_CLOCK_HT;
|
|
|
|
else
|
|
|
|
clock = BHND_CLOCK_DYN;
|
|
|
|
|
|
|
|
if ((error = bhnd_request_clock(sc->sc_dev, clock))) {
|
|
|
|
device_printf(sc->sc_dev, "%d clock request failed: %d\n",
|
|
|
|
clock, error);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
static int
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_core_init(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint64_t hf;
|
2015-10-12 04:55:20 +00:00
|
|
|
int error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(mac->mac_status == BWN_MAC_STATUS_UNINIT,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-19 04:29:25 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: called\n", __func__);
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if ((error = bwn_core_forceclk(mac, true)))
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
if (bhnd_is_hw_suspended(sc->sc_dev)) {
|
|
|
|
if ((error = bwn_reset_core(mac, mac->mac_phy.gmode)))
|
|
|
|
goto fail0;
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
mac->mac_flags &= ~BWN_MAC_FLAG_DFQVALID;
|
|
|
|
mac->mac_flags |= BWN_MAC_FLAG_RADIO_ON;
|
|
|
|
mac->mac_phy.hwpctl = (bwn_hwpctl) ? 1 : 0;
|
|
|
|
BWN_GETTIME(mac->mac_phy.nexttime);
|
|
|
|
mac->mac_phy.txerrors = BWN_TXERROR_MAX;
|
|
|
|
bzero(&mac->mac_stats, sizeof(mac->mac_stats));
|
|
|
|
mac->mac_stats.link_noise = -95;
|
|
|
|
mac->mac_reason_intr = 0;
|
|
|
|
bzero(mac->mac_reason, sizeof(mac->mac_reason));
|
|
|
|
mac->mac_intr_mask = BWN_INTR_MASKTEMPLATE;
|
|
|
|
#ifdef BWN_DEBUG
|
|
|
|
if (sc->sc_debug & BWN_DEBUG_XMIT)
|
|
|
|
mac->mac_intr_mask &= ~BWN_INTR_PHY_TXERR;
|
|
|
|
#endif
|
|
|
|
mac->mac_suspended = 1;
|
|
|
|
mac->mac_task_state = 0;
|
|
|
|
memset(&mac->mac_noise, 0, sizeof(mac->mac_noise));
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
mac->mac_phy.init_pre(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_bt_disable(mac);
|
|
|
|
if (mac->mac_phy.prepare_hw) {
|
|
|
|
error = mac->mac_phy.prepare_hw(mac);
|
|
|
|
if (error)
|
|
|
|
goto fail0;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-19 04:29:25 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: chip_init\n", __func__);
|
2016-05-02 22:58:11 +00:00
|
|
|
error = bwn_chip_init(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (error)
|
2016-05-02 22:58:11 +00:00
|
|
|
goto fail0;
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_COREREV,
|
2018-02-05 23:38:15 +00:00
|
|
|
bhnd_get_hwrev(sc->sc_dev));
|
2016-05-02 22:58:11 +00:00
|
|
|
hf = bwn_hf_read(mac);
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_G) {
|
|
|
|
hf |= BWN_HF_GPHY_SYM_WORKAROUND;
|
2018-02-05 23:38:15 +00:00
|
|
|
if (sc->sc_board_info.board_flags & BHND_BFL_PACTRL)
|
2016-05-02 22:58:11 +00:00
|
|
|
hf |= BWN_HF_PAGAINBOOST_OFDM_ON;
|
|
|
|
if (mac->mac_phy.rev == 1)
|
|
|
|
hf |= BWN_HF_GPHY_DC_CANCELFILTER;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
if (mac->mac_phy.rf_ver == 0x2050) {
|
|
|
|
if (mac->mac_phy.rf_rev < 6)
|
|
|
|
hf |= BWN_HF_FORCE_VCO_RECALC;
|
|
|
|
if (mac->mac_phy.rf_rev == 6)
|
|
|
|
hf |= BWN_HF_4318_TSSI;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2018-02-05 23:38:15 +00:00
|
|
|
if (sc->sc_board_info.board_flags & BHND_BFL_NOPLLDOWN)
|
2016-05-02 22:58:11 +00:00
|
|
|
hf |= BWN_HF_SLOWCLOCK_REQ_OFF;
|
2018-02-05 23:38:15 +00:00
|
|
|
if (sc->sc_quirks & BWN_QUIRK_UCODE_SLOWCLOCK_WAR)
|
2016-05-02 22:58:11 +00:00
|
|
|
hf |= BWN_HF_PCI_SLOWCLOCK_WORKAROUND;
|
|
|
|
hf &= ~BWN_HF_SKIP_CFP_UPDATE;
|
|
|
|
bwn_hf_write(mac, hf);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-19 05:06:48 +00:00
|
|
|
/* Tell the firmware about the MAC capabilities */
|
2018-02-05 23:38:15 +00:00
|
|
|
if (bhnd_get_hwrev(sc->sc_dev) >= 13) {
|
2016-05-19 05:06:48 +00:00
|
|
|
uint32_t cap;
|
|
|
|
cap = BWN_READ_4(mac, BWN_MAC_HW_CAP);
|
|
|
|
DPRINTF(sc, BWN_DEBUG_RESET,
|
|
|
|
"%s: hw capabilities: 0x%08x\n",
|
|
|
|
__func__, cap);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_MACHW_L,
|
|
|
|
cap & 0xffff);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_MACHW_H,
|
|
|
|
(cap >> 16) & 0xffff);
|
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_set_txretry(mac, BWN_RETRY_SHORT, BWN_RETRY_LONG);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_SHORT_RETRY_FALLBACK, 3);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_LONG_RETRY_FALLBACK, 2);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_MAXTIME, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_rate_init(mac);
|
|
|
|
bwn_set_phytxctl(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_CONT_MIN,
|
|
|
|
(mac->mac_phy.type == BWN_PHYTYPE_B) ? 0x1f : 0xf);
|
|
|
|
bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_CONT_MAX, 0x3ff);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (sc->sc_quirks & BWN_QUIRK_NODMA)
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_pio_init(mac);
|
|
|
|
else
|
|
|
|
bwn_dma_init(mac);
|
|
|
|
bwn_wme_init(mac);
|
|
|
|
bwn_spu_setdelay(mac, 1);
|
|
|
|
bwn_bt_enable(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-19 04:29:25 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: powerup\n", __func__);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (sc->sc_board_info.board_flags & BHND_BFL_NOPLLDOWN)
|
|
|
|
bwn_core_forceclk(mac, true);
|
|
|
|
else
|
|
|
|
bwn_core_forceclk(mac, false);
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_set_macaddr(mac);
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_crypt_init(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/* XXX LED initializatin */
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
mac->mac_status = BWN_MAC_STATUS_INITED;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-19 04:29:25 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: done\n", __func__);
|
2016-05-02 22:58:11 +00:00
|
|
|
return (error);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
fail0:
|
2018-02-05 23:38:15 +00:00
|
|
|
bhnd_suspend_hw(sc->sc_dev, 0);
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(mac->mac_status == BWN_MAC_STATUS_UNINIT,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
2016-05-19 04:29:25 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: fail\n", __func__);
|
2016-05-02 22:58:11 +00:00
|
|
|
return (error);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_core_start(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint32_t tmp;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(mac->mac_status == BWN_MAC_STATUS_INITED,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (bhnd_get_hwrev(sc->sc_dev) < 5)
|
2016-05-02 22:58:11 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
tmp = BWN_READ_4(mac, BWN_XMITSTAT_0);
|
|
|
|
if (!(tmp & 0x00000001))
|
|
|
|
break;
|
|
|
|
tmp = BWN_READ_4(mac, BWN_XMITSTAT_1);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
|
|
|
|
bwn_mac_enable(mac);
|
|
|
|
BWN_WRITE_4(mac, BWN_INTR_MASK, mac->mac_intr_mask);
|
|
|
|
callout_reset(&sc->sc_task_ch, hz * 15, bwn_tasks, mac);
|
|
|
|
|
|
|
|
mac->mac_status = BWN_MAC_STATUS_STARTED;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_core_exit(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint32_t macctl;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_ASSERT_LOCKED(mac->mac_sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(mac->mac_status <= BWN_MAC_STATUS_INITED,
|
2010-02-16 01:44:14 +00:00
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (mac->mac_status != BWN_MAC_STATUS_INITED)
|
|
|
|
return;
|
|
|
|
mac->mac_status = BWN_MAC_STATUS_UNINIT;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
macctl = BWN_READ_4(mac, BWN_MACCTL);
|
|
|
|
macctl &= ~BWN_MACCTL_MCODE_RUN;
|
|
|
|
macctl |= BWN_MACCTL_MCODE_JMP0;
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL, macctl);
|
|
|
|
|
|
|
|
bwn_dma_stop(mac);
|
|
|
|
bwn_pio_stop(mac);
|
|
|
|
bwn_chip_exit(mac);
|
|
|
|
mac->mac_phy.switch_analog(mac, 0);
|
2018-02-05 23:38:15 +00:00
|
|
|
bhnd_suspend_hw(sc->sc_dev, 0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_bt_disable(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
(void)sc;
|
|
|
|
/* XXX do nothing yet */
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static int
|
|
|
|
bwn_chip_init(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_phy *phy = &mac->mac_phy;
|
|
|
|
uint32_t macctl;
|
2018-02-05 23:38:15 +00:00
|
|
|
u_int delay;
|
2016-05-02 22:58:11 +00:00
|
|
|
int error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
macctl = BWN_MACCTL_IHR_ON | BWN_MACCTL_SHM_ON | BWN_MACCTL_STA;
|
|
|
|
if (phy->gmode)
|
|
|
|
macctl |= BWN_MACCTL_GMODE;
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL, macctl);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
error = bwn_fw_fillinfo(mac);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
error = bwn_fw_loaducode(mac);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
error = bwn_gpio_init(mac);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
error = bwn_fw_loadinitvals(mac);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (error)
|
2016-05-02 22:58:11 +00:00
|
|
|
return (error);
|
2018-02-05 23:38:15 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
phy->switch_analog(mac, 1);
|
|
|
|
error = bwn_phy_init(mac);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (error)
|
2016-05-02 22:58:11 +00:00
|
|
|
return (error);
|
2018-02-05 23:38:15 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (phy->set_im)
|
|
|
|
phy->set_im(mac, BWN_IMMODE_NONE);
|
|
|
|
if (phy->set_antenna)
|
|
|
|
phy->set_antenna(mac, BWN_ANT_DEFAULT);
|
|
|
|
bwn_set_txantenna(mac, BWN_ANT_DEFAULT);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (phy->type == BWN_PHYTYPE_B)
|
|
|
|
BWN_WRITE_2(mac, 0x005e, BWN_READ_2(mac, 0x005e) | 0x0004);
|
|
|
|
BWN_WRITE_4(mac, 0x0100, 0x01000000);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (bhnd_get_hwrev(sc->sc_dev) < 5)
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_4(mac, 0x010c, 0x01000000);
|
|
|
|
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL,
|
|
|
|
BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_STA);
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL,
|
|
|
|
BWN_READ_4(mac, BWN_MACCTL) | BWN_MACCTL_STA);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, 0x0074, 0x0000);
|
|
|
|
|
|
|
|
bwn_set_opmode(mac);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (bhnd_get_hwrev(sc->sc_dev) < 3) {
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_2(mac, 0x060e, 0x0000);
|
|
|
|
BWN_WRITE_2(mac, 0x0610, 0x8000);
|
|
|
|
BWN_WRITE_2(mac, 0x0604, 0x0000);
|
|
|
|
BWN_WRITE_2(mac, 0x0606, 0x0200);
|
|
|
|
} else {
|
|
|
|
BWN_WRITE_4(mac, 0x0188, 0x80000000);
|
|
|
|
BWN_WRITE_4(mac, 0x018c, 0x02000000);
|
|
|
|
}
|
|
|
|
BWN_WRITE_4(mac, BWN_INTR_REASON, 0x00004000);
|
|
|
|
BWN_WRITE_4(mac, BWN_DMA0_INTR_MASK, 0x0001dc00);
|
|
|
|
BWN_WRITE_4(mac, BWN_DMA1_INTR_MASK, 0x0000dc00);
|
|
|
|
BWN_WRITE_4(mac, BWN_DMA2_INTR_MASK, 0x0000dc00);
|
|
|
|
BWN_WRITE_4(mac, BWN_DMA3_INTR_MASK, 0x0001dc00);
|
|
|
|
BWN_WRITE_4(mac, BWN_DMA4_INTR_MASK, 0x0000dc00);
|
|
|
|
BWN_WRITE_4(mac, BWN_DMA5_INTR_MASK, 0x0000dc00);
|
2016-05-14 23:20:46 +00:00
|
|
|
|
|
|
|
bwn_mac_phy_clock_set(mac, true);
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
/* Provide the HT clock transition latency to the MAC core */
|
|
|
|
error = bhnd_get_clock_latency(sc->sc_dev, BHND_CLOCK_HT, &delay);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "failed to fetch HT clock latency: "
|
|
|
|
"%d\n", error);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (delay > UINT16_MAX) {
|
|
|
|
device_printf(sc->sc_dev, "invalid HT clock latency: %u\n",
|
|
|
|
delay);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
BWN_WRITE_2(mac, BWN_POWERUP_DELAY, delay);
|
|
|
|
return (0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/* read hostflags */
|
|
|
|
uint64_t
|
|
|
|
bwn_hf_read(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
uint64_t ret;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
ret = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_HFHI);
|
|
|
|
ret <<= 16;
|
|
|
|
ret |= bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_HFMI);
|
|
|
|
ret <<= 16;
|
|
|
|
ret |= bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_HFLO);
|
|
|
|
return (ret);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
void
|
|
|
|
bwn_hf_write(struct bwn_mac *mac, uint64_t value)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_HFLO,
|
|
|
|
(value & 0x00000000ffffull));
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_HFMI,
|
|
|
|
(value & 0x0000ffff0000ull) >> 16);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_HFHI,
|
|
|
|
(value & 0xffff00000000ULL) >> 32);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_set_txretry(struct bwn_mac *mac, int s, int l)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_SHORT_RETRY, MIN(s, 0xf));
|
|
|
|
bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_LONG_RETRY, MIN(l, 0xf));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_rate_init(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
switch (mac->mac_phy.type) {
|
|
|
|
case BWN_PHYTYPE_A:
|
|
|
|
case BWN_PHYTYPE_G:
|
|
|
|
case BWN_PHYTYPE_LP:
|
|
|
|
case BWN_PHYTYPE_N:
|
|
|
|
bwn_rate_write(mac, BWN_OFDM_RATE_6MB, 1);
|
|
|
|
bwn_rate_write(mac, BWN_OFDM_RATE_12MB, 1);
|
|
|
|
bwn_rate_write(mac, BWN_OFDM_RATE_18MB, 1);
|
|
|
|
bwn_rate_write(mac, BWN_OFDM_RATE_24MB, 1);
|
|
|
|
bwn_rate_write(mac, BWN_OFDM_RATE_36MB, 1);
|
|
|
|
bwn_rate_write(mac, BWN_OFDM_RATE_48MB, 1);
|
|
|
|
bwn_rate_write(mac, BWN_OFDM_RATE_54MB, 1);
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_A)
|
|
|
|
break;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case BWN_PHYTYPE_B:
|
|
|
|
bwn_rate_write(mac, BWN_CCK_RATE_1MB, 0);
|
|
|
|
bwn_rate_write(mac, BWN_CCK_RATE_2MB, 0);
|
|
|
|
bwn_rate_write(mac, BWN_CCK_RATE_5MB, 0);
|
|
|
|
bwn_rate_write(mac, BWN_CCK_RATE_11MB, 0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_rate_write(struct bwn_mac *mac, uint16_t rate, int ofdm)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
uint16_t offset;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (ofdm) {
|
|
|
|
offset = 0x480;
|
|
|
|
offset += (bwn_plcp_getofdm(rate) & 0x000f) * 2;
|
|
|
|
} else {
|
|
|
|
offset = 0x4c0;
|
|
|
|
offset += (bwn_plcp_getcck(rate) & 0x000f) * 2;
|
|
|
|
}
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, offset + 0x20,
|
|
|
|
bwn_shm_read_2(mac, BWN_SHARED, offset));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static uint8_t
|
|
|
|
bwn_plcp_getcck(const uint8_t bitrate)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
switch (bitrate) {
|
|
|
|
case BWN_CCK_RATE_1MB:
|
|
|
|
return (0x0a);
|
|
|
|
case BWN_CCK_RATE_2MB:
|
|
|
|
return (0x14);
|
|
|
|
case BWN_CCK_RATE_5MB:
|
|
|
|
return (0x37);
|
|
|
|
case BWN_CCK_RATE_11MB:
|
|
|
|
return (0x6e);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
2010-02-16 01:44:14 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static uint8_t
|
|
|
|
bwn_plcp_getofdm(const uint8_t bitrate)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
switch (bitrate) {
|
|
|
|
case BWN_OFDM_RATE_6MB:
|
|
|
|
return (0xb);
|
|
|
|
case BWN_OFDM_RATE_9MB:
|
|
|
|
return (0xf);
|
|
|
|
case BWN_OFDM_RATE_12MB:
|
|
|
|
return (0xa);
|
|
|
|
case BWN_OFDM_RATE_18MB:
|
|
|
|
return (0xe);
|
|
|
|
case BWN_OFDM_RATE_24MB:
|
|
|
|
return (0x9);
|
|
|
|
case BWN_OFDM_RATE_36MB:
|
|
|
|
return (0xd);
|
|
|
|
case BWN_OFDM_RATE_48MB:
|
|
|
|
return (0x8);
|
|
|
|
case BWN_OFDM_RATE_54MB:
|
|
|
|
return (0xc);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
return (0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_set_phytxctl(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
uint16_t ctl;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
ctl = (BWN_TX_PHY_ENC_CCK | BWN_TX_PHY_ANT01AUTO |
|
|
|
|
BWN_TX_PHY_TXPWR);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_BEACON_PHYCTL, ctl);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_ACKCTS_PHYCTL, ctl);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_PHYCTL, ctl);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_pio_init(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_pio *pio = &mac->mac_method.pio;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL, BWN_READ_4(mac, BWN_MACCTL)
|
|
|
|
& ~BWN_MACCTL_BIGENDIAN);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_RX_PADOFFSET, 0);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_pio_set_txqueue(mac, &pio->wme[WME_AC_BK], 0);
|
|
|
|
bwn_pio_set_txqueue(mac, &pio->wme[WME_AC_BE], 1);
|
|
|
|
bwn_pio_set_txqueue(mac, &pio->wme[WME_AC_VI], 2);
|
|
|
|
bwn_pio_set_txqueue(mac, &pio->wme[WME_AC_VO], 3);
|
|
|
|
bwn_pio_set_txqueue(mac, &pio->mcast, 4);
|
|
|
|
bwn_pio_setupqueue_rx(mac, &pio->rx, 0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_pio_set_txqueue(struct bwn_mac *mac, struct bwn_pio_txqueue *tq,
|
|
|
|
int index)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_pio_txpkt *tp;
|
2010-02-16 01:44:14 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2016-05-02 22:58:11 +00:00
|
|
|
unsigned int i;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
tq->tq_base = bwn_pio_idx2base(mac, index) + BWN_PIO_TXQOFFSET(mac);
|
|
|
|
tq->tq_index = index;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
tq->tq_free = BWN_PIO_MAX_TXPACKETS;
|
2018-02-05 23:38:15 +00:00
|
|
|
if (bhnd_get_hwrev(sc->sc_dev) >= 8)
|
2016-05-02 22:58:11 +00:00
|
|
|
tq->tq_size = 1920;
|
|
|
|
else {
|
|
|
|
tq->tq_size = bwn_pio_read_2(mac, tq, BWN_PIO_TXQBUFSIZE);
|
|
|
|
tq->tq_size -= 80;
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
TAILQ_INIT(&tq->tq_pktlist);
|
|
|
|
for (i = 0; i < N(tq->tq_pkts); i++) {
|
|
|
|
tp = &(tq->tq_pkts[i]);
|
|
|
|
tp->tp_index = i;
|
|
|
|
tp->tp_queue = tq;
|
|
|
|
TAILQ_INSERT_TAIL(&tq->tq_pktlist, tp, tp_list);
|
|
|
|
}
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static uint16_t
|
|
|
|
bwn_pio_idx2base(struct bwn_mac *mac, int index)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
static const uint16_t bases[] = {
|
|
|
|
BWN_PIO_BASE0,
|
|
|
|
BWN_PIO_BASE1,
|
|
|
|
BWN_PIO_BASE2,
|
|
|
|
BWN_PIO_BASE3,
|
|
|
|
BWN_PIO_BASE4,
|
|
|
|
BWN_PIO_BASE5,
|
|
|
|
BWN_PIO_BASE6,
|
|
|
|
BWN_PIO_BASE7,
|
|
|
|
};
|
|
|
|
static const uint16_t bases_rev11[] = {
|
|
|
|
BWN_PIO11_BASE0,
|
|
|
|
BWN_PIO11_BASE1,
|
|
|
|
BWN_PIO11_BASE2,
|
|
|
|
BWN_PIO11_BASE3,
|
|
|
|
BWN_PIO11_BASE4,
|
|
|
|
BWN_PIO11_BASE5,
|
|
|
|
};
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (bhnd_get_hwrev(sc->sc_dev) >= 11) {
|
2016-05-02 22:58:11 +00:00
|
|
|
if (index >= N(bases_rev11))
|
|
|
|
device_printf(sc->sc_dev, "%s: warning\n", __func__);
|
|
|
|
return (bases_rev11[index]);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
if (index >= N(bases))
|
|
|
|
device_printf(sc->sc_dev, "%s: warning\n", __func__);
|
|
|
|
return (bases[index]);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_pio_setupqueue_rx(struct bwn_mac *mac, struct bwn_pio_rxqueue *prq,
|
|
|
|
int index)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
prq->prq_mac = mac;
|
2018-02-05 23:38:15 +00:00
|
|
|
prq->prq_rev = bhnd_get_hwrev(sc->sc_dev);
|
2016-05-02 22:58:11 +00:00
|
|
|
prq->prq_base = bwn_pio_idx2base(mac, index) + BWN_PIO_RXQOFFSET(mac);
|
|
|
|
bwn_dma_rxdirectfifo(mac, index, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_destroy_pioqueue_tx(struct bwn_pio_txqueue *tq)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
if (tq == NULL)
|
|
|
|
return;
|
|
|
|
bwn_pio_cancel_tx_packets(tq);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_destroy_queue_tx(struct bwn_pio_txqueue *pio)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_destroy_pioqueue_tx(pio);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static uint16_t
|
|
|
|
bwn_pio_read_2(struct bwn_mac *mac, struct bwn_pio_txqueue *tq,
|
|
|
|
uint16_t offset)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
return (BWN_READ_2(mac, tq->tq_base + offset));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_rxdirectfifo(struct bwn_mac *mac, int idx, uint8_t enable)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
uint32_t ctl;
|
|
|
|
uint16_t base;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
base = bwn_dma_base(mac->mac_dmatype, idx);
|
|
|
|
if (mac->mac_dmatype == BHND_DMA_ADDR_64BIT) {
|
2016-05-02 22:58:11 +00:00
|
|
|
ctl = BWN_READ_4(mac, base + BWN_DMA64_RXCTL);
|
|
|
|
ctl &= ~BWN_DMA64_RXDIRECTFIFO;
|
|
|
|
if (enable)
|
|
|
|
ctl |= BWN_DMA64_RXDIRECTFIFO;
|
|
|
|
BWN_WRITE_4(mac, base + BWN_DMA64_RXCTL, ctl);
|
|
|
|
} else {
|
|
|
|
ctl = BWN_READ_4(mac, base + BWN_DMA32_RXCTL);
|
|
|
|
ctl &= ~BWN_DMA32_RXDIRECTFIFO;
|
|
|
|
if (enable)
|
|
|
|
ctl |= BWN_DMA32_RXDIRECTFIFO;
|
|
|
|
BWN_WRITE_4(mac, base + BWN_DMA32_RXCTL, ctl);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_pio_cancel_tx_packets(struct bwn_pio_txqueue *tq)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_pio_txpkt *tp;
|
|
|
|
unsigned int i;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
for (i = 0; i < N(tq->tq_pkts); i++) {
|
|
|
|
tp = &(tq->tq_pkts[i]);
|
|
|
|
if (tp->tp_m) {
|
|
|
|
m_freem(tp->tp_m);
|
|
|
|
tp->tp_m = NULL;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_base(int type, int controller_idx)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
static const uint16_t map64[] = {
|
|
|
|
BWN_DMA64_BASE0,
|
|
|
|
BWN_DMA64_BASE1,
|
|
|
|
BWN_DMA64_BASE2,
|
|
|
|
BWN_DMA64_BASE3,
|
|
|
|
BWN_DMA64_BASE4,
|
|
|
|
BWN_DMA64_BASE5,
|
|
|
|
};
|
|
|
|
static const uint16_t map32[] = {
|
|
|
|
BWN_DMA32_BASE0,
|
|
|
|
BWN_DMA32_BASE1,
|
|
|
|
BWN_DMA32_BASE2,
|
|
|
|
BWN_DMA32_BASE3,
|
|
|
|
BWN_DMA32_BASE4,
|
|
|
|
BWN_DMA32_BASE5,
|
2010-02-16 01:44:14 +00:00
|
|
|
};
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (type == BHND_DMA_ADDR_64BIT) {
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(controller_idx >= 0 && controller_idx < N(map64),
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
return (map64[controller_idx]);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(controller_idx >= 0 && controller_idx < N(map32),
|
2010-02-16 01:44:14 +00:00
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
2016-05-02 22:58:11 +00:00
|
|
|
return (map32[controller_idx]);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_init(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_dma *dma = &mac->mac_method.dma;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/* setup TX DMA channels. */
|
|
|
|
bwn_dma_setup(dma->wme[WME_AC_BK]);
|
|
|
|
bwn_dma_setup(dma->wme[WME_AC_BE]);
|
|
|
|
bwn_dma_setup(dma->wme[WME_AC_VI]);
|
|
|
|
bwn_dma_setup(dma->wme[WME_AC_VO]);
|
|
|
|
bwn_dma_setup(dma->mcast);
|
|
|
|
/* setup RX DMA channel. */
|
|
|
|
bwn_dma_setup(dma->rx);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static struct bwn_dma_ring *
|
|
|
|
bwn_dma_ringsetup(struct bwn_mac *mac, int controller_index,
|
2018-02-05 23:38:15 +00:00
|
|
|
int for_tx)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_dma *dma = &mac->mac_method.dma;
|
|
|
|
struct bwn_dma_ring *dr;
|
|
|
|
struct bwn_dmadesc_generic *desc;
|
|
|
|
struct bwn_dmadesc_meta *mt;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
int error, i;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
dr = malloc(sizeof(*dr), M_DEVBUF, M_NOWAIT | M_ZERO);
|
|
|
|
if (dr == NULL)
|
|
|
|
goto out;
|
|
|
|
dr->dr_numslots = BWN_RXRING_SLOTS;
|
|
|
|
if (for_tx)
|
|
|
|
dr->dr_numslots = BWN_TXRING_SLOTS;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-01-21 15:42:36 +00:00
|
|
|
dr->dr_meta = malloc(dr->dr_numslots * sizeof(struct bwn_dmadesc_meta),
|
|
|
|
M_DEVBUF, M_NOWAIT | M_ZERO);
|
2016-05-02 22:58:11 +00:00
|
|
|
if (dr->dr_meta == NULL)
|
|
|
|
goto fail0;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
dr->dr_type = mac->mac_dmatype;
|
2016-05-02 22:58:11 +00:00
|
|
|
dr->dr_mac = mac;
|
2018-02-05 23:38:15 +00:00
|
|
|
dr->dr_base = bwn_dma_base(dr->dr_type, controller_index);
|
2016-05-02 22:58:11 +00:00
|
|
|
dr->dr_index = controller_index;
|
2018-02-05 23:38:15 +00:00
|
|
|
if (dr->dr_type == BHND_DMA_ADDR_64BIT) {
|
2016-05-02 22:58:11 +00:00
|
|
|
dr->getdesc = bwn_dma_64_getdesc;
|
|
|
|
dr->setdesc = bwn_dma_64_setdesc;
|
|
|
|
dr->start_transfer = bwn_dma_64_start_transfer;
|
|
|
|
dr->suspend = bwn_dma_64_suspend;
|
|
|
|
dr->resume = bwn_dma_64_resume;
|
|
|
|
dr->get_curslot = bwn_dma_64_get_curslot;
|
|
|
|
dr->set_curslot = bwn_dma_64_set_curslot;
|
2010-02-16 01:44:14 +00:00
|
|
|
} else {
|
2016-05-02 22:58:11 +00:00
|
|
|
dr->getdesc = bwn_dma_32_getdesc;
|
|
|
|
dr->setdesc = bwn_dma_32_setdesc;
|
|
|
|
dr->start_transfer = bwn_dma_32_start_transfer;
|
|
|
|
dr->suspend = bwn_dma_32_suspend;
|
|
|
|
dr->resume = bwn_dma_32_resume;
|
|
|
|
dr->get_curslot = bwn_dma_32_get_curslot;
|
|
|
|
dr->set_curslot = bwn_dma_32_set_curslot;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
if (for_tx) {
|
|
|
|
dr->dr_tx = 1;
|
|
|
|
dr->dr_curslot = -1;
|
2010-02-16 01:44:14 +00:00
|
|
|
} else {
|
2016-05-02 22:58:11 +00:00
|
|
|
if (dr->dr_index == 0) {
|
2016-05-17 20:18:23 +00:00
|
|
|
switch (mac->mac_fw.fw_hdr_format) {
|
|
|
|
case BWN_FW_HDR_351:
|
|
|
|
case BWN_FW_HDR_410:
|
|
|
|
dr->dr_rx_bufsize =
|
|
|
|
BWN_DMA0_RX_BUFFERSIZE_FW351;
|
|
|
|
dr->dr_frameoffset =
|
|
|
|
BWN_DMA0_RX_FRAMEOFFSET_FW351;
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_598:
|
|
|
|
dr->dr_rx_bufsize =
|
|
|
|
BWN_DMA0_RX_BUFFERSIZE_FW598;
|
|
|
|
dr->dr_frameoffset =
|
|
|
|
BWN_DMA0_RX_FRAMEOFFSET_FW598;
|
|
|
|
break;
|
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
} else
|
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
error = bwn_dma_allocringmemory(dr);
|
|
|
|
if (error)
|
|
|
|
goto fail2;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (for_tx) {
|
|
|
|
/*
|
|
|
|
* Assumption: BWN_TXRING_SLOTS can be divided by
|
|
|
|
* BWN_TX_SLOTS_PER_FRAME
|
|
|
|
*/
|
|
|
|
KASSERT(BWN_TXRING_SLOTS % BWN_TX_SLOTS_PER_FRAME == 0,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-16 04:03:43 +00:00
|
|
|
dr->dr_txhdr_cache = contigmalloc(
|
|
|
|
(dr->dr_numslots / BWN_TX_SLOTS_PER_FRAME) *
|
2016-05-17 20:18:23 +00:00
|
|
|
BWN_MAXTXHDRSIZE, M_DEVBUF, M_ZERO,
|
2016-05-16 04:03:43 +00:00
|
|
|
0, BUS_SPACE_MAXADDR, 8, 0);
|
|
|
|
if (dr->dr_txhdr_cache == NULL) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"can't allocate TX header DMA memory\n");
|
|
|
|
goto fail1;
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/*
|
|
|
|
* Create TX ring DMA stuffs
|
|
|
|
*/
|
|
|
|
error = bus_dma_tag_create(dma->parent_dtag,
|
|
|
|
BWN_ALIGN, 0,
|
|
|
|
BUS_SPACE_MAXADDR,
|
|
|
|
BUS_SPACE_MAXADDR,
|
|
|
|
NULL, NULL,
|
|
|
|
BWN_HDRSIZE(mac),
|
|
|
|
1,
|
|
|
|
BUS_SPACE_MAXSIZE_32BIT,
|
|
|
|
0,
|
|
|
|
NULL, NULL,
|
|
|
|
&dr->dr_txring_dtag);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"can't create TX ring DMA tag: TODO frees\n");
|
2016-05-16 04:03:43 +00:00
|
|
|
goto fail2;
|
2016-05-02 22:58:11 +00:00
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
for (i = 0; i < dr->dr_numslots; i += 2) {
|
|
|
|
dr->getdesc(dr, i, &desc, &mt);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
mt->mt_txtype = BWN_DMADESC_METATYPE_HEADER;
|
|
|
|
mt->mt_m = NULL;
|
|
|
|
mt->mt_ni = NULL;
|
|
|
|
mt->mt_islast = 0;
|
|
|
|
error = bus_dmamap_create(dr->dr_txring_dtag, 0,
|
|
|
|
&mt->mt_dmap);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"can't create RX buf DMA map\n");
|
2016-05-16 04:03:43 +00:00
|
|
|
goto fail2;
|
2016-05-02 22:58:11 +00:00
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
dr->getdesc(dr, i + 1, &desc, &mt);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
mt->mt_txtype = BWN_DMADESC_METATYPE_BODY;
|
|
|
|
mt->mt_m = NULL;
|
|
|
|
mt->mt_ni = NULL;
|
|
|
|
mt->mt_islast = 1;
|
|
|
|
error = bus_dmamap_create(dma->txbuf_dtag, 0,
|
|
|
|
&mt->mt_dmap);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"can't create RX buf DMA map\n");
|
2016-05-16 04:03:43 +00:00
|
|
|
goto fail2;
|
2016-05-02 22:58:11 +00:00
|
|
|
}
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
} else {
|
2016-05-02 22:58:11 +00:00
|
|
|
error = bus_dmamap_create(dma->rxbuf_dtag, 0,
|
|
|
|
&dr->dr_spare_dmap);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"can't create RX buf DMA map\n");
|
|
|
|
goto out; /* XXX wrong! */
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
for (i = 0; i < dr->dr_numslots; i++) {
|
|
|
|
dr->getdesc(dr, i, &desc, &mt);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
error = bus_dmamap_create(dma->rxbuf_dtag, 0,
|
|
|
|
&mt->mt_dmap);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"can't create RX buf DMA map\n");
|
|
|
|
goto out; /* XXX wrong! */
|
|
|
|
}
|
|
|
|
error = bwn_dma_newbuf(dr, desc, mt, 1);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"failed to allocate RX buf\n");
|
|
|
|
goto out; /* XXX wrong! */
|
|
|
|
}
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bus_dmamap_sync(dr->dr_ring_dtag, dr->dr_ring_dmap,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
dr->dr_usedslot = dr->dr_numslots;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
out:
|
|
|
|
return (dr);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
fail2:
|
2016-05-16 04:03:43 +00:00
|
|
|
if (dr->dr_txhdr_cache != NULL) {
|
|
|
|
contigfree(dr->dr_txhdr_cache,
|
|
|
|
(dr->dr_numslots / BWN_TX_SLOTS_PER_FRAME) *
|
2016-05-17 20:18:23 +00:00
|
|
|
BWN_MAXTXHDRSIZE, M_DEVBUF);
|
2016-05-16 04:03:43 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
fail1:
|
|
|
|
free(dr->dr_meta, M_DEVBUF);
|
|
|
|
fail0:
|
|
|
|
free(dr, M_DEVBUF);
|
|
|
|
return (NULL);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_ringfree(struct bwn_dma_ring **dr)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (dr == NULL)
|
|
|
|
return;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_free_descbufs(*dr);
|
|
|
|
bwn_dma_free_ringmemory(*dr);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-16 04:03:43 +00:00
|
|
|
if ((*dr)->dr_txhdr_cache != NULL) {
|
|
|
|
contigfree((*dr)->dr_txhdr_cache,
|
|
|
|
((*dr)->dr_numslots / BWN_TX_SLOTS_PER_FRAME) *
|
2016-05-17 20:18:23 +00:00
|
|
|
BWN_MAXTXHDRSIZE, M_DEVBUF);
|
2016-05-16 04:03:43 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
free((*dr)->dr_meta, M_DEVBUF);
|
|
|
|
free(*dr, M_DEVBUF);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
*dr = NULL;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_32_getdesc(struct bwn_dma_ring *dr, int slot,
|
|
|
|
struct bwn_dmadesc_generic **gdesc, struct bwn_dmadesc_meta **meta)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_dmadesc32 *desc;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
*meta = &(dr->dr_meta[slot]);
|
|
|
|
desc = dr->dr_ring_descbase;
|
|
|
|
desc = &(desc[slot]);
|
|
|
|
|
|
|
|
*gdesc = (struct bwn_dmadesc_generic *)desc;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_32_setdesc(struct bwn_dma_ring *dr,
|
|
|
|
struct bwn_dmadesc_generic *desc, bus_addr_t dmaaddr, uint16_t bufsize,
|
|
|
|
int start, int end, int irq)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
struct bwn_dmadesc32 *descbase;
|
|
|
|
struct bwn_dma *dma;
|
|
|
|
struct bhnd_dma_translation *dt;
|
|
|
|
uint32_t addr, addrext, ctl;
|
|
|
|
int slot;
|
|
|
|
|
|
|
|
descbase = dr->dr_ring_descbase;
|
|
|
|
dma = &dr->dr_mac->mac_method.dma;
|
|
|
|
dt = &dma->translation;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
slot = (int)(&(desc->dma.dma32) - descbase);
|
|
|
|
KASSERT(slot >= 0 && slot < dr->dr_numslots,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
addr = (dmaaddr & dt->addr_mask) | dt->base_addr;
|
|
|
|
addrext = ((dmaaddr & dt->addrext_mask) >> dma->addrext_shift);
|
2016-05-02 22:58:11 +00:00
|
|
|
ctl = bufsize & BWN_DMA32_DCTL_BYTECNT;
|
|
|
|
if (slot == dr->dr_numslots - 1)
|
|
|
|
ctl |= BWN_DMA32_DCTL_DTABLEEND;
|
|
|
|
if (start)
|
|
|
|
ctl |= BWN_DMA32_DCTL_FRAMESTART;
|
|
|
|
if (end)
|
|
|
|
ctl |= BWN_DMA32_DCTL_FRAMEEND;
|
|
|
|
if (irq)
|
|
|
|
ctl |= BWN_DMA32_DCTL_IRQ;
|
|
|
|
ctl |= (addrext << BWN_DMA32_DCTL_ADDREXT_SHIFT)
|
|
|
|
& BWN_DMA32_DCTL_ADDREXT_MASK;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
desc->dma.dma32.control = htole32(ctl);
|
|
|
|
desc->dma.dma32.address = htole32(addr);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_32_start_transfer(struct bwn_dma_ring *dr, int slot)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA32_TXINDEX,
|
|
|
|
(uint32_t)(slot * sizeof(struct bwn_dmadesc32)));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_32_suspend(struct bwn_dma_ring *dr)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA32_TXCTL,
|
|
|
|
BWN_DMA_READ(dr, BWN_DMA32_TXCTL) | BWN_DMA32_TXSUSPEND);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_32_resume(struct bwn_dma_ring *dr)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA32_TXCTL,
|
|
|
|
BWN_DMA_READ(dr, BWN_DMA32_TXCTL) & ~BWN_DMA32_TXSUSPEND);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static int
|
|
|
|
bwn_dma_32_get_curslot(struct bwn_dma_ring *dr)
|
|
|
|
{
|
|
|
|
uint32_t val;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
val = BWN_DMA_READ(dr, BWN_DMA32_RXSTATUS);
|
|
|
|
val &= BWN_DMA32_RXDPTR;
|
|
|
|
|
|
|
|
return (val / sizeof(struct bwn_dmadesc32));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_32_set_curslot(struct bwn_dma_ring *dr, int slot)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA32_RXINDEX,
|
|
|
|
(uint32_t) (slot * sizeof(struct bwn_dmadesc32)));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_dma_64_getdesc(struct bwn_dma_ring *dr, int slot,
|
|
|
|
struct bwn_dmadesc_generic **gdesc, struct bwn_dmadesc_meta **meta)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_dmadesc64 *desc;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
*meta = &(dr->dr_meta[slot]);
|
|
|
|
desc = dr->dr_ring_descbase;
|
|
|
|
desc = &(desc[slot]);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
*gdesc = (struct bwn_dmadesc_generic *)desc;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_dma_64_setdesc(struct bwn_dma_ring *dr,
|
|
|
|
struct bwn_dmadesc_generic *desc, bus_addr_t dmaaddr, uint16_t bufsize,
|
|
|
|
int start, int end, int irq)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
struct bwn_dmadesc64 *descbase;
|
|
|
|
struct bwn_dma *dma;
|
|
|
|
struct bhnd_dma_translation *dt;
|
|
|
|
bhnd_addr_t addr;
|
|
|
|
uint32_t addrhi, addrlo;
|
|
|
|
uint32_t addrext;
|
|
|
|
uint32_t ctl0, ctl1;
|
|
|
|
int slot;
|
|
|
|
|
|
|
|
|
|
|
|
descbase = dr->dr_ring_descbase;
|
|
|
|
dma = &dr->dr_mac->mac_method.dma;
|
|
|
|
dt = &dma->translation;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
slot = (int)(&(desc->dma.dma64) - descbase);
|
|
|
|
KASSERT(slot >= 0 && slot < dr->dr_numslots,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
addr = (dmaaddr & dt->addr_mask) | dt->base_addr;
|
|
|
|
addrhi = (addr >> 32);
|
|
|
|
addrlo = (addr & UINT32_MAX);
|
|
|
|
addrext = ((dmaaddr & dt->addrext_mask) >> dma->addrext_shift);
|
|
|
|
|
|
|
|
ctl0 = 0;
|
2016-05-02 22:58:11 +00:00
|
|
|
if (slot == dr->dr_numslots - 1)
|
|
|
|
ctl0 |= BWN_DMA64_DCTL0_DTABLEEND;
|
|
|
|
if (start)
|
|
|
|
ctl0 |= BWN_DMA64_DCTL0_FRAMESTART;
|
|
|
|
if (end)
|
|
|
|
ctl0 |= BWN_DMA64_DCTL0_FRAMEEND;
|
|
|
|
if (irq)
|
|
|
|
ctl0 |= BWN_DMA64_DCTL0_IRQ;
|
2018-02-05 23:38:15 +00:00
|
|
|
|
|
|
|
ctl1 = 0;
|
2016-05-02 22:58:11 +00:00
|
|
|
ctl1 |= bufsize & BWN_DMA64_DCTL1_BYTECNT;
|
|
|
|
ctl1 |= (addrext << BWN_DMA64_DCTL1_ADDREXT_SHIFT)
|
|
|
|
& BWN_DMA64_DCTL1_ADDREXT_MASK;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
desc->dma.dma64.control0 = htole32(ctl0);
|
|
|
|
desc->dma.dma64.control1 = htole32(ctl1);
|
|
|
|
desc->dma.dma64.address_low = htole32(addrlo);
|
|
|
|
desc->dma.dma64.address_high = htole32(addrhi);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_64_start_transfer(struct bwn_dma_ring *dr, int slot)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_TXINDEX,
|
|
|
|
(uint32_t)(slot * sizeof(struct bwn_dmadesc64)));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_64_suspend(struct bwn_dma_ring *dr)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_TXCTL,
|
|
|
|
BWN_DMA_READ(dr, BWN_DMA64_TXCTL) | BWN_DMA64_TXSUSPEND);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_64_resume(struct bwn_dma_ring *dr)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_TXCTL,
|
|
|
|
BWN_DMA_READ(dr, BWN_DMA64_TXCTL) & ~BWN_DMA64_TXSUSPEND);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static int
|
|
|
|
bwn_dma_64_get_curslot(struct bwn_dma_ring *dr)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
uint32_t val;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
val = BWN_DMA_READ(dr, BWN_DMA64_RXSTATUS);
|
|
|
|
val &= BWN_DMA64_RXSTATDPTR;
|
|
|
|
|
|
|
|
return (val / sizeof(struct bwn_dmadesc64));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_64_set_curslot(struct bwn_dma_ring *dr, int slot)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_RXINDEX,
|
|
|
|
(uint32_t)(slot * sizeof(struct bwn_dmadesc64)));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_allocringmemory(struct bwn_dma_ring *dr)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_mac *mac = dr->dr_mac;
|
|
|
|
struct bwn_dma *dma = &mac->mac_method.dma;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = bus_dma_tag_create(dma->parent_dtag,
|
|
|
|
BWN_ALIGN, 0,
|
|
|
|
BUS_SPACE_MAXADDR,
|
|
|
|
BUS_SPACE_MAXADDR,
|
|
|
|
NULL, NULL,
|
|
|
|
BWN_DMA_RINGMEMSIZE,
|
|
|
|
1,
|
|
|
|
BUS_SPACE_MAXSIZE_32BIT,
|
|
|
|
0,
|
|
|
|
NULL, NULL,
|
|
|
|
&dr->dr_ring_dtag);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"can't create TX ring DMA tag: TODO frees\n");
|
|
|
|
return (-1);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
error = bus_dmamem_alloc(dr->dr_ring_dtag,
|
|
|
|
&dr->dr_ring_descbase, BUS_DMA_WAITOK | BUS_DMA_ZERO,
|
|
|
|
&dr->dr_ring_dmap);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"can't allocate DMA mem: TODO frees\n");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
error = bus_dmamap_load(dr->dr_ring_dtag, dr->dr_ring_dmap,
|
|
|
|
dr->dr_ring_descbase, BWN_DMA_RINGMEMSIZE,
|
|
|
|
bwn_dma_ring_addr, &dr->dr_ring_dmabase, BUS_DMA_NOWAIT);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"can't load DMA mem: TODO free\n");
|
|
|
|
return (-1);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
return (0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_setup(struct bwn_dma_ring *dr)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
struct bwn_mac *mac;
|
|
|
|
struct bwn_dma *dma;
|
|
|
|
struct bhnd_dma_translation *dt;
|
|
|
|
bhnd_addr_t addr, paddr;
|
|
|
|
uint32_t addrhi, addrlo, addrext, value;
|
|
|
|
|
|
|
|
mac = dr->dr_mac;
|
|
|
|
dma = &mac->mac_method.dma;
|
|
|
|
dt = &dma->translation;
|
|
|
|
|
|
|
|
paddr = dr->dr_ring_dmabase;
|
|
|
|
addr = (paddr & dt->addr_mask) | dt->base_addr;
|
|
|
|
addrhi = (addr >> 32);
|
|
|
|
addrlo = (addr & UINT32_MAX);
|
|
|
|
addrext = ((paddr & dt->addrext_mask) >> dma->addrext_shift);
|
2016-05-02 22:58:11 +00:00
|
|
|
|
|
|
|
if (dr->dr_tx) {
|
|
|
|
dr->dr_curslot = -1;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (dr->dr_type == BHND_DMA_ADDR_64BIT) {
|
2016-05-02 22:58:11 +00:00
|
|
|
value = BWN_DMA64_TXENABLE;
|
2017-12-14 06:45:04 +00:00
|
|
|
value |= BWN_DMA64_TXPARITY_DISABLE;
|
2016-05-02 22:58:11 +00:00
|
|
|
value |= (addrext << BWN_DMA64_TXADDREXT_SHIFT)
|
|
|
|
& BWN_DMA64_TXADDREXT_MASK;
|
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_TXCTL, value);
|
2018-02-05 23:38:15 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_TXRINGLO, addrlo);
|
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_TXRINGHI, addrhi);
|
2016-05-02 22:58:11 +00:00
|
|
|
} else {
|
|
|
|
value = BWN_DMA32_TXENABLE;
|
2017-12-14 06:45:04 +00:00
|
|
|
value |= BWN_DMA32_TXPARITY_DISABLE;
|
2016-05-02 22:58:11 +00:00
|
|
|
value |= (addrext << BWN_DMA32_TXADDREXT_SHIFT)
|
|
|
|
& BWN_DMA32_TXADDREXT_MASK;
|
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA32_TXCTL, value);
|
2018-02-05 23:38:15 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA32_TXRING, addrlo);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set for RX
|
|
|
|
*/
|
|
|
|
dr->dr_usedslot = dr->dr_numslots;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (dr->dr_type == BHND_DMA_ADDR_64BIT) {
|
2016-05-02 22:58:11 +00:00
|
|
|
value = (dr->dr_frameoffset << BWN_DMA64_RXFROFF_SHIFT);
|
|
|
|
value |= BWN_DMA64_RXENABLE;
|
2017-12-14 06:45:04 +00:00
|
|
|
value |= BWN_DMA64_RXPARITY_DISABLE;
|
2016-05-02 22:58:11 +00:00
|
|
|
value |= (addrext << BWN_DMA64_RXADDREXT_SHIFT)
|
|
|
|
& BWN_DMA64_RXADDREXT_MASK;
|
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_RXCTL, value);
|
2018-02-05 23:38:15 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_RXRINGLO, addrlo);
|
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_RXRINGHI, addrhi);
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_RXINDEX, dr->dr_numslots *
|
|
|
|
sizeof(struct bwn_dmadesc64));
|
|
|
|
} else {
|
|
|
|
value = (dr->dr_frameoffset << BWN_DMA32_RXFROFF_SHIFT);
|
|
|
|
value |= BWN_DMA32_RXENABLE;
|
2017-12-14 06:45:04 +00:00
|
|
|
value |= BWN_DMA32_RXPARITY_DISABLE;
|
2016-05-02 22:58:11 +00:00
|
|
|
value |= (addrext << BWN_DMA32_RXADDREXT_SHIFT)
|
|
|
|
& BWN_DMA32_RXADDREXT_MASK;
|
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA32_RXCTL, value);
|
2018-02-05 23:38:15 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA32_RXRING, addrlo);
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA32_RXINDEX, dr->dr_numslots *
|
|
|
|
sizeof(struct bwn_dmadesc32));
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_dma_free_ringmemory(struct bwn_dma_ring *dr)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bus_dmamap_unload(dr->dr_ring_dtag, dr->dr_ring_dmap);
|
|
|
|
bus_dmamem_free(dr->dr_ring_dtag, dr->dr_ring_descbase,
|
|
|
|
dr->dr_ring_dmap);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_dma_cleanup(struct bwn_dma_ring *dr)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (dr->dr_tx) {
|
|
|
|
bwn_dma_tx_reset(dr->dr_mac, dr->dr_base, dr->dr_type);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (dr->dr_type == BHND_DMA_ADDR_64BIT) {
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_TXRINGLO, 0);
|
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_TXRINGHI, 0);
|
|
|
|
} else
|
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA32_TXRING, 0);
|
|
|
|
} else {
|
|
|
|
bwn_dma_rx_reset(dr->dr_mac, dr->dr_base, dr->dr_type);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (dr->dr_type == BHND_DMA_ADDR_64BIT) {
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_RXRINGLO, 0);
|
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA64_RXRINGHI, 0);
|
|
|
|
} else
|
|
|
|
BWN_DMA_WRITE(dr, BWN_DMA32_RXRING, 0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_free_descbufs(struct bwn_dma_ring *dr)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_dmadesc_generic *desc;
|
|
|
|
struct bwn_dmadesc_meta *meta;
|
|
|
|
struct bwn_mac *mac = dr->dr_mac;
|
|
|
|
struct bwn_dma *dma = &mac->mac_method.dma;
|
2010-02-16 01:44:14 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
int i;
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (!dr->dr_usedslot)
|
2010-02-16 01:44:14 +00:00
|
|
|
return;
|
2016-05-02 22:58:11 +00:00
|
|
|
for (i = 0; i < dr->dr_numslots; i++) {
|
|
|
|
dr->getdesc(dr, i, &desc, &meta);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (meta->mt_m == NULL) {
|
|
|
|
if (!dr->dr_tx)
|
|
|
|
device_printf(sc->sc_dev, "%s: not TX?\n",
|
|
|
|
__func__);
|
2010-02-16 01:44:14 +00:00
|
|
|
continue;
|
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
if (dr->dr_tx) {
|
|
|
|
if (meta->mt_txtype == BWN_DMADESC_METATYPE_HEADER)
|
|
|
|
bus_dmamap_unload(dr->dr_txring_dtag,
|
|
|
|
meta->mt_dmap);
|
|
|
|
else if (meta->mt_txtype == BWN_DMADESC_METATYPE_BODY)
|
|
|
|
bus_dmamap_unload(dma->txbuf_dtag,
|
|
|
|
meta->mt_dmap);
|
|
|
|
} else
|
|
|
|
bus_dmamap_unload(dma->rxbuf_dtag, meta->mt_dmap);
|
|
|
|
bwn_dma_free_descbuf(dr, meta);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static int
|
|
|
|
bwn_dma_tx_reset(struct bwn_mac *mac, uint16_t base,
|
|
|
|
int type)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint32_t value;
|
|
|
|
int i;
|
|
|
|
uint16_t offset;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
for (i = 0; i < 10; i++) {
|
2018-02-05 23:38:15 +00:00
|
|
|
offset = (type == BHND_DMA_ADDR_64BIT) ? BWN_DMA64_TXSTATUS :
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA32_TXSTATUS;
|
|
|
|
value = BWN_READ_4(mac, base + offset);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (type == BHND_DMA_ADDR_64BIT) {
|
2016-05-02 22:58:11 +00:00
|
|
|
value &= BWN_DMA64_TXSTAT;
|
|
|
|
if (value == BWN_DMA64_TXSTAT_DISABLED ||
|
|
|
|
value == BWN_DMA64_TXSTAT_IDLEWAIT ||
|
|
|
|
value == BWN_DMA64_TXSTAT_STOPPED)
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
value &= BWN_DMA32_TXSTATE;
|
|
|
|
if (value == BWN_DMA32_TXSTAT_DISABLED ||
|
|
|
|
value == BWN_DMA32_TXSTAT_IDLEWAIT ||
|
|
|
|
value == BWN_DMA32_TXSTAT_STOPPED)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
DELAY(1000);
|
|
|
|
}
|
2018-02-05 23:38:15 +00:00
|
|
|
offset = (type == BHND_DMA_ADDR_64BIT) ? BWN_DMA64_TXCTL :
|
|
|
|
BWN_DMA32_TXCTL;
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_4(mac, base + offset, 0);
|
|
|
|
for (i = 0; i < 10; i++) {
|
2018-02-05 23:38:15 +00:00
|
|
|
offset = (type == BHND_DMA_ADDR_64BIT) ? BWN_DMA64_TXSTATUS :
|
|
|
|
BWN_DMA32_TXSTATUS;
|
2016-05-02 22:58:11 +00:00
|
|
|
value = BWN_READ_4(mac, base + offset);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (type == BHND_DMA_ADDR_64BIT) {
|
2016-05-02 22:58:11 +00:00
|
|
|
value &= BWN_DMA64_TXSTAT;
|
|
|
|
if (value == BWN_DMA64_TXSTAT_DISABLED) {
|
|
|
|
i = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
value &= BWN_DMA32_TXSTATE;
|
|
|
|
if (value == BWN_DMA32_TXSTAT_DISABLED) {
|
|
|
|
i = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DELAY(1000);
|
|
|
|
}
|
|
|
|
if (i != -1) {
|
|
|
|
device_printf(sc->sc_dev, "%s: timed out\n", __func__);
|
|
|
|
return (ENODEV);
|
|
|
|
}
|
|
|
|
DELAY(1000);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
return (0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static int
|
|
|
|
bwn_dma_rx_reset(struct bwn_mac *mac, uint16_t base,
|
|
|
|
int type)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2016-05-02 22:58:11 +00:00
|
|
|
uint32_t value;
|
2010-02-16 01:44:14 +00:00
|
|
|
int i;
|
2016-05-02 22:58:11 +00:00
|
|
|
uint16_t offset;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
offset = (type == BHND_DMA_ADDR_64BIT) ? BWN_DMA64_RXCTL :
|
|
|
|
BWN_DMA32_RXCTL;
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_4(mac, base + offset, 0);
|
|
|
|
for (i = 0; i < 10; i++) {
|
2018-02-05 23:38:15 +00:00
|
|
|
offset = (type == BHND_DMA_ADDR_64BIT) ? BWN_DMA64_RXSTATUS :
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_DMA32_RXSTATUS;
|
|
|
|
value = BWN_READ_4(mac, base + offset);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (type == BHND_DMA_ADDR_64BIT) {
|
2016-05-02 22:58:11 +00:00
|
|
|
value &= BWN_DMA64_RXSTAT;
|
|
|
|
if (value == BWN_DMA64_RXSTAT_DISABLED) {
|
|
|
|
i = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
value &= BWN_DMA32_RXSTATE;
|
|
|
|
if (value == BWN_DMA32_RXSTAT_DISABLED) {
|
|
|
|
i = -1;
|
|
|
|
break;
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
DELAY(1000);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
if (i != -1) {
|
|
|
|
device_printf(sc->sc_dev, "%s: timed out\n", __func__);
|
|
|
|
return (ENODEV);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_dma_free_descbuf(struct bwn_dma_ring *dr,
|
|
|
|
struct bwn_dmadesc_meta *meta)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (meta->mt_m != NULL) {
|
|
|
|
m_freem(meta->mt_m);
|
|
|
|
meta->mt_m = NULL;
|
|
|
|
}
|
|
|
|
if (meta->mt_ni != NULL) {
|
|
|
|
ieee80211_free_node(meta->mt_ni);
|
|
|
|
meta->mt_ni = NULL;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_set_redzone(struct bwn_dma_ring *dr, struct mbuf *m)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_rxhdr4 *rxhdr;
|
|
|
|
unsigned char *frame;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
rxhdr = mtod(m, struct bwn_rxhdr4 *);
|
|
|
|
rxhdr->frame_len = 0;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(dr->dr_rx_bufsize >= dr->dr_frameoffset +
|
|
|
|
sizeof(struct bwn_plcp6) + 2,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
frame = mtod(m, char *) + dr->dr_frameoffset;
|
|
|
|
memset(frame, 0xff, sizeof(struct bwn_plcp6) + 2 /* padding */);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static uint8_t
|
|
|
|
bwn_dma_check_redzone(struct bwn_dma_ring *dr, struct mbuf *m)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
unsigned char *f = mtod(m, char *) + dr->dr_frameoffset;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
return ((f[0] & f[1] & f[2] & f[3] & f[4] & f[5] & f[6] & f[7])
|
|
|
|
== 0xff);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_wme_init(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_wme_load(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/* enable WME support. */
|
|
|
|
bwn_hf_write(mac, bwn_hf_read(mac) | BWN_HF_EDCF);
|
|
|
|
BWN_WRITE_2(mac, BWN_IFSCTL, BWN_READ_2(mac, BWN_IFSCTL) |
|
|
|
|
BWN_IFSCTL_USE_EDCF);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_spu_setdelay(struct bwn_mac *mac, int idle)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
|
|
uint16_t delay; /* microsec */
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
delay = (mac->mac_phy.type == BWN_PHYTYPE_A) ? 3700 : 1050;
|
|
|
|
if (ic->ic_opmode == IEEE80211_M_IBSS || idle)
|
|
|
|
delay = 500;
|
|
|
|
if ((mac->mac_phy.rf_ver == 0x2050) && (mac->mac_phy.rf_rev == 8))
|
|
|
|
delay = max(delay, (uint16_t)2400);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_SPU_WAKEUP, delay);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_bt_enable(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint64_t hf;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (bwn_bluetooth == 0)
|
|
|
|
return;
|
2018-02-05 23:38:15 +00:00
|
|
|
if ((sc->sc_board_info.board_flags & BHND_BFL_BTCOEX) == 0)
|
2016-05-02 22:58:11 +00:00
|
|
|
return;
|
|
|
|
if (mac->mac_phy.type != BWN_PHYTYPE_B && !mac->mac_phy.gmode)
|
2010-02-16 01:44:14 +00:00
|
|
|
return;
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
hf = bwn_hf_read(mac);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (sc->sc_board_info.board_flags & BHND_BFL_BTC2WIRE_ALTGPIO)
|
2016-05-02 22:58:11 +00:00
|
|
|
hf |= BWN_HF_BT_COEXISTALT;
|
2010-02-16 01:44:14 +00:00
|
|
|
else
|
2016-05-02 22:58:11 +00:00
|
|
|
hf |= BWN_HF_BT_COEXIST;
|
|
|
|
bwn_hf_write(mac, hf);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_set_macaddr(struct bwn_mac *mac)
|
|
|
|
{
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_mac_write_bssid(mac);
|
|
|
|
bwn_mac_setfilter(mac, BWN_MACFILTER_SELF,
|
|
|
|
mac->mac_sc->sc_ic.ic_macaddr);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_clear_keys(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
int i;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
for (i = 0; i < mac->mac_max_nr_keys; i++) {
|
|
|
|
KASSERT(i >= 0 && i < mac->mac_max_nr_keys,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_key_dowrite(mac, i, BWN_SEC_ALGO_NONE,
|
|
|
|
NULL, BWN_SEC_KEYSIZE, NULL);
|
|
|
|
if ((i <= 3) && !BWN_SEC_NEWAPI(mac)) {
|
|
|
|
bwn_key_dowrite(mac, i + 4, BWN_SEC_ALGO_NONE,
|
|
|
|
NULL, BWN_SEC_KEYSIZE, NULL);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
mac->mac_key[i].keyconf = NULL;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_crypt_init(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
mac->mac_max_nr_keys = (bhnd_get_hwrev(sc->sc_dev) >= 5) ? 58 : 20;
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(mac->mac_max_nr_keys <= N(mac->mac_key),
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
mac->mac_ktp = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_KEY_TABLEP);
|
|
|
|
mac->mac_ktp *= 2;
|
2018-02-05 23:38:15 +00:00
|
|
|
if (bhnd_get_hwrev(sc->sc_dev) >= 5)
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_2(mac, BWN_RCMTA_COUNT, mac->mac_max_nr_keys - 8);
|
|
|
|
bwn_clear_keys(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_chip_exit(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
bwn_phy_exit(mac);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static int
|
|
|
|
bwn_fw_fillinfo(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
int error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
error = bwn_fw_gets(mac, BWN_FWTYPE_DEFAULT);
|
|
|
|
if (error == 0)
|
|
|
|
return (0);
|
|
|
|
error = bwn_fw_gets(mac, BWN_FWTYPE_OPENSOURCE);
|
|
|
|
if (error == 0)
|
|
|
|
return (0);
|
|
|
|
return (error);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
/**
|
|
|
|
* Request that the GPIO controller tristate all pins set in @p mask, granting
|
|
|
|
* the MAC core control over the pins.
|
|
|
|
*
|
|
|
|
* @param mac bwn MAC state.
|
|
|
|
* @param pins If the bit position for a pin number is set to one, tristate the
|
|
|
|
* pin.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
bwn_gpio_control(struct bwn_mac *mac, uint32_t pins)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc;
|
|
|
|
uint32_t flags[32];
|
|
|
|
int error;
|
|
|
|
|
|
|
|
sc = mac->mac_sc;
|
|
|
|
|
|
|
|
/* Determine desired pin flags */
|
|
|
|
for (size_t pin = 0; pin < nitems(flags); pin++) {
|
|
|
|
uint32_t pinbit = (1 << pin);
|
|
|
|
|
|
|
|
if (pins & pinbit) {
|
|
|
|
/* Tristate output */
|
|
|
|
flags[pin] = GPIO_PIN_OUTPUT|GPIO_PIN_TRISTATE;
|
|
|
|
} else {
|
|
|
|
/* Leave unmodified */
|
|
|
|
flags[pin] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure all pins */
|
|
|
|
error = GPIO_PIN_CONFIG_32(sc->sc_gpio, 0, nitems(flags), flags);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "error configuring %s pin flags: "
|
|
|
|
"%d\n", device_get_nameunit(sc->sc_gpio), error);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static int
|
|
|
|
bwn_gpio_init(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
struct bwn_softc *sc;
|
|
|
|
uint32_t pins;
|
|
|
|
|
|
|
|
sc = mac->mac_sc;
|
|
|
|
|
|
|
|
pins = 0xF;
|
2016-05-02 22:58:11 +00:00
|
|
|
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL,
|
|
|
|
BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_GPOUT_MASK);
|
|
|
|
BWN_WRITE_2(mac, BWN_GPIO_MASK,
|
2018-02-05 23:38:15 +00:00
|
|
|
BWN_READ_2(mac, BWN_GPIO_MASK) | pins);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (sc->sc_board_info.board_flags & BHND_BFL_PACTRL) {
|
|
|
|
/* MAC core is responsible for toggling PAREF via gpio9 */
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_2(mac, BWN_GPIO_MASK,
|
2018-02-05 23:38:15 +00:00
|
|
|
BWN_READ_2(mac, BWN_GPIO_MASK) | BHND_GPIO_BOARD_PACTRL);
|
2016-05-02 22:58:11 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
pins |= BHND_GPIO_BOARD_PACTRL;
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
return (bwn_gpio_control(mac, pins));
|
2016-05-02 22:58:11 +00:00
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static int
|
|
|
|
bwn_fw_loadinitvals(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
#define GETFWOFFSET(fwp, offset) \
|
|
|
|
((const struct bwn_fwinitvals *)((const char *)fwp.fw->data + offset))
|
|
|
|
const size_t hdr_len = sizeof(struct bwn_fwhdr);
|
|
|
|
const struct bwn_fwhdr *hdr;
|
|
|
|
struct bwn_fw *fw = &mac->mac_fw;
|
|
|
|
int error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
hdr = (const struct bwn_fwhdr *)(fw->initvals.fw->data);
|
|
|
|
error = bwn_fwinitvals_write(mac, GETFWOFFSET(fw->initvals, hdr_len),
|
|
|
|
be32toh(hdr->size), fw->initvals.fw->datasize - hdr_len);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
if (fw->initvals_band.fw) {
|
|
|
|
hdr = (const struct bwn_fwhdr *)(fw->initvals_band.fw->data);
|
|
|
|
error = bwn_fwinitvals_write(mac,
|
|
|
|
GETFWOFFSET(fw->initvals_band, hdr_len),
|
|
|
|
be32toh(hdr->size),
|
|
|
|
fw->initvals_band.fw->datasize - hdr_len);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
return (error);
|
|
|
|
#undef GETFWOFFSET
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static int
|
|
|
|
bwn_phy_init(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
int error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
mac->mac_phy.chan = mac->mac_phy.get_default_chan(mac);
|
|
|
|
mac->mac_phy.rf_onoff(mac, 1);
|
|
|
|
error = mac->mac_phy.init(mac);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "PHY init failed\n");
|
|
|
|
goto fail0;
|
|
|
|
}
|
|
|
|
error = bwn_switch_channel(mac,
|
|
|
|
mac->mac_phy.get_default_chan(mac));
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"failed to switch default channel\n");
|
|
|
|
goto fail1;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
return (0);
|
|
|
|
fail1:
|
|
|
|
if (mac->mac_phy.exit)
|
|
|
|
mac->mac_phy.exit(mac);
|
|
|
|
fail0:
|
|
|
|
mac->mac_phy.rf_onoff(mac, 0);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
return (error);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
static void
|
|
|
|
bwn_set_txantenna(struct bwn_mac *mac, int antenna)
|
|
|
|
{
|
|
|
|
uint16_t ant;
|
|
|
|
uint16_t tmp;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
ant = bwn_ant2phy(antenna);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/* For ACK/CTS */
|
|
|
|
tmp = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_ACKCTS_PHYCTL);
|
|
|
|
tmp = (tmp & ~BWN_TX_PHY_ANT) | ant;
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_ACKCTS_PHYCTL, tmp);
|
|
|
|
/* For Probe Resposes */
|
|
|
|
tmp = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_PHYCTL);
|
|
|
|
tmp = (tmp & ~BWN_TX_PHY_ANT) | ant;
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_PHYCTL, tmp);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_set_opmode(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2010-03-09 19:58:00 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2016-05-02 22:58:11 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
|
|
uint32_t ctl;
|
|
|
|
uint16_t cfp_pretbtt;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
ctl = BWN_READ_4(mac, BWN_MACCTL);
|
|
|
|
ctl &= ~(BWN_MACCTL_HOSTAP | BWN_MACCTL_PASS_CTL |
|
|
|
|
BWN_MACCTL_PASS_BADPLCP | BWN_MACCTL_PASS_BADFCS |
|
|
|
|
BWN_MACCTL_PROMISC | BWN_MACCTL_BEACON_PROMISC);
|
|
|
|
ctl |= BWN_MACCTL_STA;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
|
|
|
|
ic->ic_opmode == IEEE80211_M_MBSS)
|
|
|
|
ctl |= BWN_MACCTL_HOSTAP;
|
|
|
|
else if (ic->ic_opmode == IEEE80211_M_IBSS)
|
|
|
|
ctl &= ~BWN_MACCTL_STA;
|
|
|
|
ctl |= sc->sc_filters;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (bhnd_get_hwrev(sc->sc_dev) <= 4)
|
2016-05-02 22:58:11 +00:00
|
|
|
ctl |= BWN_MACCTL_PROMISC;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL, ctl);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
cfp_pretbtt = 2;
|
|
|
|
if ((ctl & BWN_MACCTL_STA) && !(ctl & BWN_MACCTL_HOSTAP)) {
|
2018-02-05 23:38:15 +00:00
|
|
|
if (sc->sc_cid.chip_id == BHND_CHIPID_BCM4306 &&
|
|
|
|
sc->sc_cid.chip_rev == 3)
|
2016-05-02 22:58:11 +00:00
|
|
|
cfp_pretbtt = 100;
|
2010-02-16 01:44:14 +00:00
|
|
|
else
|
2016-05-02 22:58:11 +00:00
|
|
|
cfp_pretbtt = 50;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_2(mac, 0x612, cfp_pretbtt);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-05-02 22:58:11 +00:00
|
|
|
bwn_dma_ring_addr(void *arg, bus_dma_segment_t *seg, int nseg, int error)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
if (!error) {
|
|
|
|
KASSERT(nseg == 1, ("too many segments(%d)\n", nseg));
|
|
|
|
*((bus_addr_t *)arg) = seg->ds_addr;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-03 03:24:27 +00:00
|
|
|
void
|
|
|
|
bwn_dummy_transmission(struct bwn_mac *mac, int ofdm, int paon)
|
|
|
|
{
|
|
|
|
struct bwn_phy *phy = &mac->mac_phy;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
unsigned int i, max_loop;
|
|
|
|
uint16_t value;
|
|
|
|
uint32_t buffer[5] = {
|
|
|
|
0x00000000, 0x00d40000, 0x00000000, 0x01000000, 0x00000000
|
|
|
|
};
|
|
|
|
|
|
|
|
if (ofdm) {
|
|
|
|
max_loop = 0x1e;
|
|
|
|
buffer[0] = 0x000201cc;
|
|
|
|
} else {
|
|
|
|
max_loop = 0xfa;
|
|
|
|
buffer[0] = 0x000b846e;
|
|
|
|
}
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(mac->mac_sc);
|
|
|
|
|
|
|
|
for (i = 0; i < 5; i++)
|
|
|
|
bwn_ram_write(mac, i * 4, buffer[i]);
|
|
|
|
|
|
|
|
BWN_WRITE_2(mac, 0x0568, 0x0000);
|
|
|
|
BWN_WRITE_2(mac, 0x07c0,
|
2018-02-05 23:38:15 +00:00
|
|
|
(bhnd_get_hwrev(sc->sc_dev) < 11) ? 0x0000 : 0x0100);
|
2016-05-03 03:31:16 +00:00
|
|
|
|
|
|
|
value = (ofdm ? 0x41 : 0x40);
|
2016-05-03 03:24:27 +00:00
|
|
|
BWN_WRITE_2(mac, 0x050c, value);
|
2016-05-03 03:31:16 +00:00
|
|
|
|
|
|
|
if (phy->type == BWN_PHYTYPE_N || phy->type == BWN_PHYTYPE_LP ||
|
|
|
|
phy->type == BWN_PHYTYPE_LCN)
|
2016-05-03 03:24:27 +00:00
|
|
|
BWN_WRITE_2(mac, 0x0514, 0x1a02);
|
|
|
|
BWN_WRITE_2(mac, 0x0508, 0x0000);
|
|
|
|
BWN_WRITE_2(mac, 0x050a, 0x0000);
|
|
|
|
BWN_WRITE_2(mac, 0x054c, 0x0000);
|
|
|
|
BWN_WRITE_2(mac, 0x056a, 0x0014);
|
|
|
|
BWN_WRITE_2(mac, 0x0568, 0x0826);
|
|
|
|
BWN_WRITE_2(mac, 0x0500, 0x0000);
|
2016-05-03 03:31:16 +00:00
|
|
|
|
|
|
|
/* XXX TODO: n phy pa override? */
|
|
|
|
|
|
|
|
switch (phy->type) {
|
|
|
|
case BWN_PHYTYPE_N:
|
|
|
|
case BWN_PHYTYPE_LCN:
|
|
|
|
BWN_WRITE_2(mac, 0x0502, 0x00d0);
|
|
|
|
break;
|
|
|
|
case BWN_PHYTYPE_LP:
|
2016-05-03 03:24:27 +00:00
|
|
|
BWN_WRITE_2(mac, 0x0502, 0x0050);
|
2016-05-03 03:31:16 +00:00
|
|
|
break;
|
|
|
|
default:
|
2016-05-03 03:24:27 +00:00
|
|
|
BWN_WRITE_2(mac, 0x0502, 0x0030);
|
2016-05-03 03:31:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* flush */
|
|
|
|
BWN_READ_2(mac, 0x0502);
|
2016-05-03 03:24:27 +00:00
|
|
|
|
|
|
|
if (phy->rf_ver == 0x2050 && phy->rf_rev <= 0x5)
|
|
|
|
BWN_RF_WRITE(mac, 0x0051, 0x0017);
|
|
|
|
for (i = 0x00; i < max_loop; i++) {
|
|
|
|
value = BWN_READ_2(mac, 0x050e);
|
|
|
|
if (value & 0x0080)
|
|
|
|
break;
|
|
|
|
DELAY(10);
|
|
|
|
}
|
|
|
|
for (i = 0x00; i < 0x0a; i++) {
|
|
|
|
value = BWN_READ_2(mac, 0x050e);
|
|
|
|
if (value & 0x0400)
|
|
|
|
break;
|
|
|
|
DELAY(10);
|
|
|
|
}
|
|
|
|
for (i = 0x00; i < 0x19; i++) {
|
|
|
|
value = BWN_READ_2(mac, 0x0690);
|
|
|
|
if (!(value & 0x0100))
|
|
|
|
break;
|
|
|
|
DELAY(10);
|
|
|
|
}
|
|
|
|
if (phy->rf_ver == 0x2050 && phy->rf_rev <= 0x5)
|
|
|
|
BWN_RF_WRITE(mac, 0x0051, 0x0037);
|
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
void
|
|
|
|
bwn_ram_write(struct bwn_mac *mac, uint16_t offset, uint32_t val)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
uint32_t macctl;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(offset % 4 == 0, ("%s:%d: fail", __func__, __LINE__));
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
macctl = BWN_READ_4(mac, BWN_MACCTL);
|
|
|
|
if (macctl & BWN_MACCTL_BIGENDIAN)
|
|
|
|
printf("TODO: need swap\n");
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_4(mac, BWN_RAM_CONTROL, offset);
|
2018-02-05 23:38:15 +00:00
|
|
|
BWN_BARRIER(mac, BWN_RAM_CONTROL, 4, BUS_SPACE_BARRIER_WRITE);
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_4(mac, BWN_RAM_DATA, val);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
void
|
|
|
|
bwn_mac_suspend(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
int i;
|
|
|
|
uint32_t tmp;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(mac->mac_suspended >= 0,
|
2010-02-16 01:44:14 +00:00
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
2016-05-19 04:29:25 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: suspended=%d\n",
|
|
|
|
__func__, mac->mac_suspended);
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
if (mac->mac_suspended == 0) {
|
|
|
|
bwn_psctl(mac, BWN_PS_AWAKE);
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL,
|
|
|
|
BWN_READ_4(mac, BWN_MACCTL)
|
|
|
|
& ~BWN_MACCTL_ON);
|
|
|
|
BWN_READ_4(mac, BWN_MACCTL);
|
|
|
|
for (i = 35; i; i--) {
|
|
|
|
tmp = BWN_READ_4(mac, BWN_INTR_REASON);
|
|
|
|
if (tmp & BWN_INTR_MAC_SUSPENDED)
|
|
|
|
goto out;
|
|
|
|
DELAY(10);
|
|
|
|
}
|
|
|
|
for (i = 40; i; i--) {
|
|
|
|
tmp = BWN_READ_4(mac, BWN_INTR_REASON);
|
|
|
|
if (tmp & BWN_INTR_MAC_SUSPENDED)
|
|
|
|
goto out;
|
|
|
|
DELAY(1000);
|
|
|
|
}
|
|
|
|
device_printf(sc->sc_dev, "MAC suspend failed\n");
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
out:
|
|
|
|
mac->mac_suspended++;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
void
|
|
|
|
bwn_mac_enable(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2016-05-02 22:58:11 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint16_t state;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-19 04:29:25 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: suspended=%d\n",
|
|
|
|
__func__, mac->mac_suspended);
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
state = bwn_shm_read_2(mac, BWN_SHARED,
|
|
|
|
BWN_SHARED_UCODESTAT);
|
|
|
|
if (state != BWN_SHARED_UCODESTAT_SUSPEND &&
|
2016-05-19 05:09:20 +00:00
|
|
|
state != BWN_SHARED_UCODESTAT_SLEEP) {
|
|
|
|
DPRINTF(sc, BWN_DEBUG_FW,
|
|
|
|
"%s: warn: firmware state (%d)\n",
|
|
|
|
__func__, state);
|
|
|
|
}
|
2016-05-02 22:58:11 +00:00
|
|
|
|
|
|
|
mac->mac_suspended--;
|
|
|
|
KASSERT(mac->mac_suspended >= 0,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
if (mac->mac_suspended == 0) {
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL,
|
|
|
|
BWN_READ_4(mac, BWN_MACCTL) | BWN_MACCTL_ON);
|
|
|
|
BWN_WRITE_4(mac, BWN_INTR_REASON, BWN_INTR_MAC_SUSPENDED);
|
|
|
|
BWN_READ_4(mac, BWN_MACCTL);
|
|
|
|
BWN_READ_4(mac, BWN_INTR_REASON);
|
|
|
|
bwn_psctl(mac, 0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
void
|
|
|
|
bwn_psctl(struct bwn_mac *mac, uint32_t flags)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2010-03-09 19:58:00 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2016-05-02 22:58:11 +00:00
|
|
|
int i;
|
|
|
|
uint16_t ucstat;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
KASSERT(!((flags & BWN_PS_ON) && (flags & BWN_PS_OFF)),
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
KASSERT(!((flags & BWN_PS_AWAKE) && (flags & BWN_PS_ASLEEP)),
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
/* XXX forcibly awake and hwps-off */
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL,
|
|
|
|
(BWN_READ_4(mac, BWN_MACCTL) | BWN_MACCTL_AWAKE) &
|
|
|
|
~BWN_MACCTL_HWPS);
|
|
|
|
BWN_READ_4(mac, BWN_MACCTL);
|
2018-02-05 23:38:15 +00:00
|
|
|
if (bhnd_get_hwrev(sc->sc_dev) >= 5) {
|
2016-05-02 22:58:11 +00:00
|
|
|
for (i = 0; i < 100; i++) {
|
|
|
|
ucstat = bwn_shm_read_2(mac, BWN_SHARED,
|
|
|
|
BWN_SHARED_UCODESTAT);
|
|
|
|
if (ucstat != BWN_SHARED_UCODESTAT_SLEEP)
|
2010-02-16 01:44:14 +00:00
|
|
|
break;
|
2016-05-02 22:58:11 +00:00
|
|
|
DELAY(10);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
}
|
2016-05-18 05:56:25 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: ucstat=%d\n", __func__,
|
|
|
|
ucstat);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_fw_gets(struct bwn_mac *mac, enum bwn_fwtype type)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
struct bwn_fw *fw = &mac->mac_fw;
|
2018-02-05 23:38:15 +00:00
|
|
|
const uint8_t rev = bhnd_get_hwrev(sc->sc_dev);
|
2010-02-16 01:44:14 +00:00
|
|
|
const char *filename;
|
2018-02-05 23:38:15 +00:00
|
|
|
uint16_t iost;
|
2010-02-16 01:44:14 +00:00
|
|
|
int error;
|
|
|
|
|
|
|
|
/* microcode */
|
2016-05-14 23:23:50 +00:00
|
|
|
filename = NULL;
|
|
|
|
switch (rev) {
|
|
|
|
case 42:
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_AC)
|
|
|
|
filename = "ucode42";
|
|
|
|
break;
|
|
|
|
case 40:
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_AC)
|
|
|
|
filename = "ucode40";
|
|
|
|
break;
|
|
|
|
case 33:
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_LCN40)
|
|
|
|
filename = "ucode33_lcn40";
|
|
|
|
break;
|
|
|
|
case 30:
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_N)
|
|
|
|
filename = "ucode30_mimo";
|
|
|
|
break;
|
|
|
|
case 29:
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_HT)
|
|
|
|
filename = "ucode29_mimo";
|
|
|
|
break;
|
|
|
|
case 26:
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_HT)
|
|
|
|
filename = "ucode26_mimo";
|
|
|
|
break;
|
|
|
|
case 28:
|
|
|
|
case 25:
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_N)
|
|
|
|
filename = "ucode25_mimo";
|
|
|
|
else if (mac->mac_phy.type == BWN_PHYTYPE_LCN)
|
|
|
|
filename = "ucode25_lcn";
|
|
|
|
break;
|
|
|
|
case 24:
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_LCN)
|
|
|
|
filename = "ucode24_lcn";
|
|
|
|
break;
|
|
|
|
case 23:
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_N)
|
|
|
|
filename = "ucode16_mimo";
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
case 17:
|
|
|
|
case 18:
|
|
|
|
case 19:
|
|
|
|
if (mac->mac_phy.type == BWN_PHYTYPE_N)
|
|
|
|
filename = "ucode16_mimo";
|
|
|
|
else if (mac->mac_phy.type == BWN_PHYTYPE_LP)
|
|
|
|
filename = "ucode16_lp";
|
|
|
|
break;
|
|
|
|
case 15:
|
2010-02-16 01:44:14 +00:00
|
|
|
filename = "ucode15";
|
2016-05-14 23:23:50 +00:00
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
filename = "ucode14";
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
filename = "ucode13";
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
case 11:
|
|
|
|
filename = "ucode11";
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
case 9:
|
|
|
|
case 8:
|
|
|
|
case 7:
|
|
|
|
case 6:
|
|
|
|
case 5:
|
|
|
|
filename = "ucode5";
|
|
|
|
break;
|
|
|
|
default:
|
2010-02-16 01:44:14 +00:00
|
|
|
device_printf(sc->sc_dev, "no ucode for rev %d\n", rev);
|
|
|
|
bwn_release_firmware(mac);
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
}
|
2016-05-14 23:23:50 +00:00
|
|
|
|
|
|
|
device_printf(sc->sc_dev, "ucode fw: %s\n", filename);
|
2010-02-16 01:44:14 +00:00
|
|
|
error = bwn_fw_get(mac, type, filename, &fw->ucode);
|
|
|
|
if (error) {
|
|
|
|
bwn_release_firmware(mac);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PCM */
|
|
|
|
KASSERT(fw->no_pcmfile == 0, ("%s:%d fail", __func__, __LINE__));
|
|
|
|
if (rev >= 5 && rev <= 10) {
|
|
|
|
error = bwn_fw_get(mac, type, "pcm5", &fw->pcm);
|
|
|
|
if (error == ENOENT)
|
|
|
|
fw->no_pcmfile = 1;
|
|
|
|
else if (error) {
|
|
|
|
bwn_release_firmware(mac);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
} else if (rev < 11) {
|
|
|
|
device_printf(sc->sc_dev, "no PCM for rev %d\n", rev);
|
2016-06-05 07:56:28 +00:00
|
|
|
bwn_release_firmware(mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
return (EOPNOTSUPP);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initvals */
|
2018-02-05 23:38:15 +00:00
|
|
|
error = bhnd_read_iost(sc->sc_dev, &iost);
|
|
|
|
if (error)
|
|
|
|
goto fail1;
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
switch (mac->mac_phy.type) {
|
|
|
|
case BWN_PHYTYPE_A:
|
|
|
|
if (rev < 5 || rev > 10)
|
|
|
|
goto fail1;
|
2018-02-05 23:38:15 +00:00
|
|
|
if (iost & BWN_IOST_HAVE_2GHZ)
|
2010-02-16 01:44:14 +00:00
|
|
|
filename = "a0g1initvals5";
|
|
|
|
else
|
|
|
|
filename = "a0g0initvals5";
|
|
|
|
break;
|
|
|
|
case BWN_PHYTYPE_G:
|
|
|
|
if (rev >= 5 && rev <= 10)
|
|
|
|
filename = "b0g0initvals5";
|
|
|
|
else if (rev >= 13)
|
|
|
|
filename = "b0g0initvals13";
|
|
|
|
else
|
|
|
|
goto fail1;
|
|
|
|
break;
|
|
|
|
case BWN_PHYTYPE_LP:
|
|
|
|
if (rev == 13)
|
|
|
|
filename = "lp0initvals13";
|
|
|
|
else if (rev == 14)
|
|
|
|
filename = "lp0initvals14";
|
|
|
|
else if (rev >= 15)
|
|
|
|
filename = "lp0initvals15";
|
|
|
|
else
|
|
|
|
goto fail1;
|
|
|
|
break;
|
|
|
|
case BWN_PHYTYPE_N:
|
2016-05-14 23:23:50 +00:00
|
|
|
if (rev == 30)
|
|
|
|
filename = "n16initvals30";
|
|
|
|
else if (rev == 28 || rev == 25)
|
|
|
|
filename = "n0initvals25";
|
|
|
|
else if (rev == 24)
|
|
|
|
filename = "n0initvals24";
|
|
|
|
else if (rev == 23)
|
|
|
|
filename = "n0initvals16";
|
|
|
|
else if (rev >= 16 && rev <= 18)
|
|
|
|
filename = "n0initvals16";
|
|
|
|
else if (rev >= 11 && rev <= 12)
|
2010-02-16 01:44:14 +00:00
|
|
|
filename = "n0initvals11";
|
|
|
|
else
|
|
|
|
goto fail1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto fail1;
|
|
|
|
}
|
|
|
|
error = bwn_fw_get(mac, type, filename, &fw->initvals);
|
|
|
|
if (error) {
|
|
|
|
bwn_release_firmware(mac);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bandswitch initvals */
|
|
|
|
switch (mac->mac_phy.type) {
|
|
|
|
case BWN_PHYTYPE_A:
|
|
|
|
if (rev >= 5 && rev <= 10) {
|
2018-02-05 23:38:15 +00:00
|
|
|
if (iost & BWN_IOST_HAVE_2GHZ)
|
2010-02-16 01:44:14 +00:00
|
|
|
filename = "a0g1bsinitvals5";
|
|
|
|
else
|
|
|
|
filename = "a0g0bsinitvals5";
|
|
|
|
} else if (rev >= 11)
|
|
|
|
filename = NULL;
|
|
|
|
else
|
|
|
|
goto fail1;
|
|
|
|
break;
|
|
|
|
case BWN_PHYTYPE_G:
|
|
|
|
if (rev >= 5 && rev <= 10)
|
|
|
|
filename = "b0g0bsinitvals5";
|
|
|
|
else if (rev >= 11)
|
|
|
|
filename = NULL;
|
|
|
|
else
|
|
|
|
goto fail1;
|
|
|
|
break;
|
|
|
|
case BWN_PHYTYPE_LP:
|
|
|
|
if (rev == 13)
|
|
|
|
filename = "lp0bsinitvals13";
|
|
|
|
else if (rev == 14)
|
|
|
|
filename = "lp0bsinitvals14";
|
|
|
|
else if (rev >= 15)
|
|
|
|
filename = "lp0bsinitvals15";
|
|
|
|
else
|
|
|
|
goto fail1;
|
|
|
|
break;
|
|
|
|
case BWN_PHYTYPE_N:
|
2016-05-14 23:23:50 +00:00
|
|
|
if (rev == 30)
|
|
|
|
filename = "n16bsinitvals30";
|
|
|
|
else if (rev == 28 || rev == 25)
|
|
|
|
filename = "n0bsinitvals25";
|
|
|
|
else if (rev == 24)
|
|
|
|
filename = "n0bsinitvals24";
|
|
|
|
else if (rev == 23)
|
|
|
|
filename = "n0bsinitvals16";
|
|
|
|
else if (rev >= 16 && rev <= 18)
|
|
|
|
filename = "n0bsinitvals16";
|
|
|
|
else if (rev >= 11 && rev <= 12)
|
2010-02-16 01:44:14 +00:00
|
|
|
filename = "n0bsinitvals11";
|
|
|
|
else
|
|
|
|
goto fail1;
|
|
|
|
break;
|
|
|
|
default:
|
2016-05-14 23:23:50 +00:00
|
|
|
device_printf(sc->sc_dev, "unknown phy (%d)\n",
|
|
|
|
mac->mac_phy.type);
|
2010-02-16 01:44:14 +00:00
|
|
|
goto fail1;
|
|
|
|
}
|
|
|
|
error = bwn_fw_get(mac, type, filename, &fw->initvals_band);
|
|
|
|
if (error) {
|
|
|
|
bwn_release_firmware(mac);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
fail1:
|
2016-05-14 23:23:50 +00:00
|
|
|
device_printf(sc->sc_dev, "no INITVALS for rev %d, phy.type %d\n",
|
|
|
|
rev, mac->mac_phy.type);
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_release_firmware(mac);
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_fw_get(struct bwn_mac *mac, enum bwn_fwtype type,
|
|
|
|
const char *name, struct bwn_fwfile *bfw)
|
|
|
|
{
|
|
|
|
const struct bwn_fwhdr *hdr;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
const struct firmware *fw;
|
|
|
|
char namebuf[64];
|
|
|
|
|
|
|
|
if (name == NULL) {
|
|
|
|
bwn_do_release_fw(bfw);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
if (bfw->filename != NULL) {
|
|
|
|
if (bfw->type == type && (strcmp(bfw->filename, name) == 0))
|
|
|
|
return (0);
|
|
|
|
bwn_do_release_fw(bfw);
|
|
|
|
}
|
|
|
|
|
2010-02-27 23:42:32 +00:00
|
|
|
snprintf(namebuf, sizeof(namebuf), "bwn%s_v4_%s%s",
|
|
|
|
(type == BWN_FWTYPE_OPENSOURCE) ? "-open" : "",
|
|
|
|
(mac->mac_phy.type == BWN_PHYTYPE_LP) ? "lp_" : "", name);
|
2010-02-16 01:44:14 +00:00
|
|
|
/* XXX Sleeping on "fwload" with the non-sleepable locks held */
|
|
|
|
fw = firmware_get(namebuf);
|
|
|
|
if (fw == NULL) {
|
|
|
|
device_printf(sc->sc_dev, "the fw file(%s) not found\n",
|
|
|
|
namebuf);
|
|
|
|
return (ENOENT);
|
|
|
|
}
|
|
|
|
if (fw->datasize < sizeof(struct bwn_fwhdr))
|
|
|
|
goto fail;
|
|
|
|
hdr = (const struct bwn_fwhdr *)(fw->data);
|
|
|
|
switch (hdr->type) {
|
|
|
|
case BWN_FWTYPE_UCODE:
|
|
|
|
case BWN_FWTYPE_PCM:
|
|
|
|
if (be32toh(hdr->size) !=
|
|
|
|
(fw->datasize - sizeof(struct bwn_fwhdr)))
|
|
|
|
goto fail;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case BWN_FWTYPE_IV:
|
|
|
|
if (hdr->ver != 1)
|
|
|
|
goto fail;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
bfw->filename = name;
|
|
|
|
bfw->fw = fw;
|
|
|
|
bfw->type = type;
|
|
|
|
return (0);
|
|
|
|
fail:
|
|
|
|
device_printf(sc->sc_dev, "the fw file(%s) format error\n", namebuf);
|
|
|
|
if (fw != NULL)
|
|
|
|
firmware_put(fw, FIRMWARE_UNLOAD);
|
|
|
|
return (EPROTO);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_release_firmware(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
|
|
|
|
bwn_do_release_fw(&mac->mac_fw.ucode);
|
|
|
|
bwn_do_release_fw(&mac->mac_fw.pcm);
|
|
|
|
bwn_do_release_fw(&mac->mac_fw.initvals);
|
|
|
|
bwn_do_release_fw(&mac->mac_fw.initvals_band);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_do_release_fw(struct bwn_fwfile *bfw)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (bfw->fw != NULL)
|
|
|
|
firmware_put(bfw->fw, FIRMWARE_UNLOAD);
|
|
|
|
bfw->fw = NULL;
|
|
|
|
bfw->filename = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_fw_loaducode(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
#define GETFWOFFSET(fwp, offset) \
|
|
|
|
((const uint32_t *)((const char *)fwp.fw->data + offset))
|
|
|
|
#define GETFWSIZE(fwp, offset) \
|
|
|
|
((fwp.fw->datasize - offset) / sizeof(uint32_t))
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
const uint32_t *data;
|
|
|
|
unsigned int i;
|
|
|
|
uint32_t ctl;
|
|
|
|
uint16_t date, fwcaps, time;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
ctl = BWN_READ_4(mac, BWN_MACCTL);
|
|
|
|
ctl |= BWN_MACCTL_MCODE_JMP0;
|
|
|
|
KASSERT(!(ctl & BWN_MACCTL_MCODE_RUN), ("%s:%d: fail", __func__,
|
|
|
|
__LINE__));
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL, ctl);
|
|
|
|
for (i = 0; i < 64; i++)
|
|
|
|
bwn_shm_write_2(mac, BWN_SCRATCH, i, 0);
|
|
|
|
for (i = 0; i < 4096; i += 2)
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, i, 0);
|
|
|
|
|
|
|
|
data = GETFWOFFSET(mac->mac_fw.ucode, sizeof(struct bwn_fwhdr));
|
|
|
|
bwn_shm_ctlword(mac, BWN_UCODE | BWN_SHARED_AUTOINC, 0x0000);
|
|
|
|
for (i = 0; i < GETFWSIZE(mac->mac_fw.ucode, sizeof(struct bwn_fwhdr));
|
|
|
|
i++) {
|
|
|
|
BWN_WRITE_4(mac, BWN_SHM_DATA, be32toh(data[i]));
|
|
|
|
DELAY(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mac->mac_fw.pcm.fw) {
|
|
|
|
data = GETFWOFFSET(mac->mac_fw.pcm, sizeof(struct bwn_fwhdr));
|
|
|
|
bwn_shm_ctlword(mac, BWN_HW, 0x01ea);
|
|
|
|
BWN_WRITE_4(mac, BWN_SHM_DATA, 0x00004000);
|
|
|
|
bwn_shm_ctlword(mac, BWN_HW, 0x01eb);
|
|
|
|
for (i = 0; i < GETFWSIZE(mac->mac_fw.pcm,
|
|
|
|
sizeof(struct bwn_fwhdr)); i++) {
|
|
|
|
BWN_WRITE_4(mac, BWN_SHM_DATA, be32toh(data[i]));
|
|
|
|
DELAY(10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BWN_WRITE_4(mac, BWN_INTR_REASON, BWN_INTR_ALL);
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL,
|
|
|
|
(BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_MCODE_JMP0) |
|
|
|
|
BWN_MACCTL_MCODE_RUN);
|
|
|
|
|
|
|
|
for (i = 0; i < 21; i++) {
|
|
|
|
if (BWN_READ_4(mac, BWN_INTR_REASON) == BWN_INTR_MAC_SUSPENDED)
|
|
|
|
break;
|
|
|
|
if (i >= 20) {
|
|
|
|
device_printf(sc->sc_dev, "ucode timeout\n");
|
|
|
|
error = ENXIO;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
DELAY(50000);
|
|
|
|
}
|
|
|
|
BWN_READ_4(mac, BWN_INTR_REASON);
|
|
|
|
|
|
|
|
mac->mac_fw.rev = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_UCODE_REV);
|
|
|
|
if (mac->mac_fw.rev <= 0x128) {
|
|
|
|
device_printf(sc->sc_dev, "the firmware is too old\n");
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
goto error;
|
|
|
|
}
|
2016-05-05 07:04:38 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine firmware header version; needed for TX/RX packet
|
|
|
|
* handling.
|
|
|
|
*/
|
|
|
|
if (mac->mac_fw.rev >= 598)
|
|
|
|
mac->mac_fw.fw_hdr_format = BWN_FW_HDR_598;
|
|
|
|
else if (mac->mac_fw.rev >= 410)
|
|
|
|
mac->mac_fw.fw_hdr_format = BWN_FW_HDR_410;
|
|
|
|
else
|
|
|
|
mac->mac_fw.fw_hdr_format = BWN_FW_HDR_351;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't support rev 598 or later; that requires
|
|
|
|
* another round of changes to the TX/RX descriptor
|
|
|
|
* and status layout.
|
|
|
|
*
|
|
|
|
* So, complain this is the case and exit out, rather
|
|
|
|
* than attaching and then failing.
|
|
|
|
*/
|
2016-05-18 05:56:25 +00:00
|
|
|
#if 0
|
2016-05-05 07:04:38 +00:00
|
|
|
if (mac->mac_fw.fw_hdr_format == BWN_FW_HDR_598) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"firmware is too new (>=598); not supported\n");
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
goto error;
|
|
|
|
}
|
2016-05-18 05:56:25 +00:00
|
|
|
#endif
|
2016-05-05 07:04:38 +00:00
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
mac->mac_fw.patch = bwn_shm_read_2(mac, BWN_SHARED,
|
|
|
|
BWN_SHARED_UCODE_PATCH);
|
|
|
|
date = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_UCODE_DATE);
|
|
|
|
mac->mac_fw.opensource = (date == 0xffff);
|
|
|
|
if (bwn_wme != 0)
|
|
|
|
mac->mac_flags |= BWN_MAC_FLAG_WME;
|
|
|
|
mac->mac_flags |= BWN_MAC_FLAG_HWCRYPTO;
|
|
|
|
|
|
|
|
time = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_UCODE_TIME);
|
|
|
|
if (mac->mac_fw.opensource == 0) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"firmware version (rev %u patch %u date %#x time %#x)\n",
|
|
|
|
mac->mac_fw.rev, mac->mac_fw.patch, date, time);
|
|
|
|
if (mac->mac_fw.no_pcmfile)
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"no HW crypto acceleration due to pcm5\n");
|
|
|
|
} else {
|
|
|
|
mac->mac_fw.patch = time;
|
|
|
|
fwcaps = bwn_fwcaps_read(mac);
|
|
|
|
if (!(fwcaps & BWN_FWCAPS_HWCRYPTO) || mac->mac_fw.no_pcmfile) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"disabling HW crypto acceleration\n");
|
|
|
|
mac->mac_flags &= ~BWN_MAC_FLAG_HWCRYPTO;
|
|
|
|
}
|
|
|
|
if (!(fwcaps & BWN_FWCAPS_WME)) {
|
|
|
|
device_printf(sc->sc_dev, "disabling WME support\n");
|
|
|
|
mac->mac_flags &= ~BWN_MAC_FLAG_WME;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BWN_ISOLDFMT(mac))
|
|
|
|
device_printf(sc->sc_dev, "using old firmware image\n");
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
error:
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCTL,
|
|
|
|
(BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_MCODE_RUN) |
|
|
|
|
BWN_MACCTL_MCODE_JMP0);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
#undef GETFWSIZE
|
|
|
|
#undef GETFWOFFSET
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OpenFirmware only */
|
|
|
|
static uint16_t
|
|
|
|
bwn_fwcaps_read(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
|
|
|
|
KASSERT(mac->mac_fw.opensource == 1,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
return (bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_FWCAPS));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_fwinitvals_write(struct bwn_mac *mac, const struct bwn_fwinitvals *ivals,
|
|
|
|
size_t count, size_t array_size)
|
|
|
|
{
|
|
|
|
#define GET_NEXTIV16(iv) \
|
|
|
|
((const struct bwn_fwinitvals *)((const uint8_t *)(iv) + \
|
|
|
|
sizeof(uint16_t) + sizeof(uint16_t)))
|
|
|
|
#define GET_NEXTIV32(iv) \
|
|
|
|
((const struct bwn_fwinitvals *)((const uint8_t *)(iv) + \
|
|
|
|
sizeof(uint16_t) + sizeof(uint32_t)))
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
const struct bwn_fwinitvals *iv;
|
|
|
|
uint16_t offset;
|
|
|
|
size_t i;
|
|
|
|
uint8_t bit32;
|
|
|
|
|
|
|
|
KASSERT(sizeof(struct bwn_fwinitvals) == 6,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
iv = ivals;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
if (array_size < sizeof(iv->offset_size))
|
|
|
|
goto fail;
|
|
|
|
array_size -= sizeof(iv->offset_size);
|
|
|
|
offset = be16toh(iv->offset_size);
|
|
|
|
bit32 = (offset & BWN_FWINITVALS_32BIT) ? 1 : 0;
|
|
|
|
offset &= BWN_FWINITVALS_OFFSET_MASK;
|
|
|
|
if (offset >= 0x1000)
|
|
|
|
goto fail;
|
|
|
|
if (bit32) {
|
|
|
|
if (array_size < sizeof(iv->data.d32))
|
|
|
|
goto fail;
|
|
|
|
array_size -= sizeof(iv->data.d32);
|
|
|
|
BWN_WRITE_4(mac, offset, be32toh(iv->data.d32));
|
|
|
|
iv = GET_NEXTIV32(iv);
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (array_size < sizeof(iv->data.d16))
|
|
|
|
goto fail;
|
|
|
|
array_size -= sizeof(iv->data.d16);
|
|
|
|
BWN_WRITE_2(mac, offset, be16toh(iv->data.d16));
|
|
|
|
|
|
|
|
iv = GET_NEXTIV16(iv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (array_size != 0)
|
|
|
|
goto fail;
|
|
|
|
return (0);
|
|
|
|
fail:
|
|
|
|
device_printf(sc->sc_dev, "initvals: invalid format\n");
|
|
|
|
return (EPROTO);
|
|
|
|
#undef GET_NEXTIV16
|
|
|
|
#undef GET_NEXTIV32
|
|
|
|
}
|
|
|
|
|
2016-05-02 22:58:11 +00:00
|
|
|
int
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_switch_channel(struct bwn_mac *mac, int chan)
|
|
|
|
{
|
|
|
|
struct bwn_phy *phy = &(mac->mac_phy);
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2010-02-16 01:44:14 +00:00
|
|
|
uint16_t channelcookie, savedcookie;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (chan == 0xffff)
|
|
|
|
chan = phy->get_default_chan(mac);
|
|
|
|
|
|
|
|
channelcookie = chan;
|
|
|
|
if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
|
|
|
|
channelcookie |= 0x100;
|
|
|
|
savedcookie = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_CHAN);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_CHAN, channelcookie);
|
|
|
|
error = phy->switch_channel(mac, chan);
|
|
|
|
if (error)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
mac->mac_phy.chan = chan;
|
|
|
|
DELAY(8000);
|
|
|
|
return (0);
|
|
|
|
fail:
|
|
|
|
device_printf(sc->sc_dev, "failed to switch channel\n");
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_CHAN, savedcookie);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
bwn_ant2phy(int antenna)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch (antenna) {
|
|
|
|
case BWN_ANT0:
|
|
|
|
return (BWN_TX_PHY_ANT0);
|
|
|
|
case BWN_ANT1:
|
|
|
|
return (BWN_TX_PHY_ANT1);
|
|
|
|
case BWN_ANT2:
|
|
|
|
return (BWN_TX_PHY_ANT2);
|
|
|
|
case BWN_ANT3:
|
|
|
|
return (BWN_TX_PHY_ANT3);
|
|
|
|
case BWN_ANTAUTO:
|
|
|
|
return (BWN_TX_PHY_ANT01AUTO);
|
|
|
|
}
|
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_wme_load(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
KASSERT(N(bwn_wme_shm_offsets) == N(sc->sc_wmeParams),
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
|
|
|
bwn_mac_suspend(mac);
|
|
|
|
for (i = 0; i < N(sc->sc_wmeParams); i++)
|
|
|
|
bwn_wme_loadparams(mac, &(sc->sc_wmeParams[i]),
|
|
|
|
bwn_wme_shm_offsets[i]);
|
|
|
|
bwn_mac_enable(mac);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_wme_loadparams(struct bwn_mac *mac,
|
|
|
|
const struct wmeParams *p, uint16_t shm_offset)
|
|
|
|
{
|
|
|
|
#define SM(_v, _f) (((_v) << _f##_S) & _f)
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint16_t params[BWN_NR_WMEPARAMS];
|
|
|
|
int slot, tmp;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
slot = BWN_READ_2(mac, BWN_RNG) &
|
|
|
|
SM(p->wmep_logcwmin, WME_PARAM_LOGCWMIN);
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
|
|
DPRINTF(sc, BWN_DEBUG_WME, "wmep_txopLimit %d wmep_logcwmin %d "
|
|
|
|
"wmep_logcwmax %d wmep_aifsn %d\n", p->wmep_txopLimit,
|
|
|
|
p->wmep_logcwmin, p->wmep_logcwmax, p->wmep_aifsn);
|
|
|
|
|
|
|
|
params[BWN_WMEPARAM_TXOP] = p->wmep_txopLimit * 32;
|
|
|
|
params[BWN_WMEPARAM_CWMIN] = SM(p->wmep_logcwmin, WME_PARAM_LOGCWMIN);
|
|
|
|
params[BWN_WMEPARAM_CWMAX] = SM(p->wmep_logcwmax, WME_PARAM_LOGCWMAX);
|
|
|
|
params[BWN_WMEPARAM_CWCUR] = SM(p->wmep_logcwmin, WME_PARAM_LOGCWMIN);
|
|
|
|
params[BWN_WMEPARAM_AIFS] = p->wmep_aifsn;
|
|
|
|
params[BWN_WMEPARAM_BSLOTS] = slot;
|
|
|
|
params[BWN_WMEPARAM_REGGAP] = slot + p->wmep_aifsn;
|
|
|
|
|
|
|
|
for (i = 0; i < N(params); i++) {
|
|
|
|
if (i == BWN_WMEPARAM_STATUS) {
|
|
|
|
tmp = bwn_shm_read_2(mac, BWN_SHARED,
|
|
|
|
shm_offset + (i * 2));
|
|
|
|
tmp |= 0x100;
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, shm_offset + (i * 2),
|
|
|
|
tmp);
|
|
|
|
} else {
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, shm_offset + (i * 2),
|
|
|
|
params[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_mac_write_bssid(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint32_t tmp;
|
|
|
|
int i;
|
|
|
|
uint8_t mac_bssid[IEEE80211_ADDR_LEN * 2];
|
|
|
|
|
|
|
|
bwn_mac_setfilter(mac, BWN_MACFILTER_BSSID, sc->sc_bssid);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
memcpy(mac_bssid, sc->sc_ic.ic_macaddr, IEEE80211_ADDR_LEN);
|
2010-02-16 01:44:14 +00:00
|
|
|
memcpy(mac_bssid + IEEE80211_ADDR_LEN, sc->sc_bssid,
|
|
|
|
IEEE80211_ADDR_LEN);
|
|
|
|
|
|
|
|
for (i = 0; i < N(mac_bssid); i += sizeof(uint32_t)) {
|
|
|
|
tmp = (uint32_t) (mac_bssid[i + 0]);
|
|
|
|
tmp |= (uint32_t) (mac_bssid[i + 1]) << 8;
|
|
|
|
tmp |= (uint32_t) (mac_bssid[i + 2]) << 16;
|
|
|
|
tmp |= (uint32_t) (mac_bssid[i + 3]) << 24;
|
|
|
|
bwn_ram_write(mac, 0x20 + i, tmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_mac_setfilter(struct bwn_mac *mac, uint16_t offset,
|
|
|
|
const uint8_t *macaddr)
|
|
|
|
{
|
|
|
|
static const uint8_t zero[IEEE80211_ADDR_LEN] = { 0 };
|
|
|
|
uint16_t data;
|
|
|
|
|
|
|
|
if (!mac)
|
|
|
|
macaddr = zero;
|
|
|
|
|
|
|
|
offset |= 0x0020;
|
|
|
|
BWN_WRITE_2(mac, BWN_MACFILTER_CONTROL, offset);
|
|
|
|
|
|
|
|
data = macaddr[0];
|
|
|
|
data |= macaddr[1] << 8;
|
|
|
|
BWN_WRITE_2(mac, BWN_MACFILTER_DATA, data);
|
|
|
|
data = macaddr[2];
|
|
|
|
data |= macaddr[3] << 8;
|
|
|
|
BWN_WRITE_2(mac, BWN_MACFILTER_DATA, data);
|
|
|
|
data = macaddr[4];
|
|
|
|
data |= macaddr[5] << 8;
|
|
|
|
BWN_WRITE_2(mac, BWN_MACFILTER_DATA, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_key_dowrite(struct bwn_mac *mac, uint8_t index, uint8_t algorithm,
|
|
|
|
const uint8_t *key, size_t key_len, const uint8_t *mac_addr)
|
|
|
|
{
|
|
|
|
uint8_t buf[BWN_SEC_KEYSIZE] = { 0, };
|
|
|
|
uint8_t per_sta_keys_start = 8;
|
|
|
|
|
|
|
|
if (BWN_SEC_NEWAPI(mac))
|
|
|
|
per_sta_keys_start = 4;
|
|
|
|
|
|
|
|
KASSERT(index < mac->mac_max_nr_keys,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
KASSERT(key_len <= BWN_SEC_KEYSIZE,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
|
|
|
if (index >= per_sta_keys_start)
|
|
|
|
bwn_key_macwrite(mac, index, NULL);
|
|
|
|
if (key)
|
|
|
|
memcpy(buf, key, key_len);
|
|
|
|
bwn_key_write(mac, index, algorithm, buf);
|
|
|
|
if (index >= per_sta_keys_start)
|
|
|
|
bwn_key_macwrite(mac, index, mac_addr);
|
|
|
|
|
|
|
|
mac->mac_key[index].algorithm = algorithm;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_key_macwrite(struct bwn_mac *mac, uint8_t index, const uint8_t *addr)
|
|
|
|
{
|
2010-03-09 19:58:00 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2010-02-16 01:44:14 +00:00
|
|
|
uint32_t addrtmp[2] = { 0, 0 };
|
|
|
|
uint8_t start = 8;
|
|
|
|
|
|
|
|
if (BWN_SEC_NEWAPI(mac))
|
|
|
|
start = 4;
|
|
|
|
|
|
|
|
KASSERT(index >= start,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
index -= start;
|
|
|
|
|
|
|
|
if (addr) {
|
|
|
|
addrtmp[0] = addr[0];
|
|
|
|
addrtmp[0] |= ((uint32_t) (addr[1]) << 8);
|
|
|
|
addrtmp[0] |= ((uint32_t) (addr[2]) << 16);
|
|
|
|
addrtmp[0] |= ((uint32_t) (addr[3]) << 24);
|
|
|
|
addrtmp[1] = addr[4];
|
|
|
|
addrtmp[1] |= ((uint32_t) (addr[5]) << 8);
|
|
|
|
}
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (bhnd_get_hwrev(sc->sc_dev) >= 5) {
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_shm_write_4(mac, BWN_RCMTA, (index * 2) + 0, addrtmp[0]);
|
|
|
|
bwn_shm_write_2(mac, BWN_RCMTA, (index * 2) + 1, addrtmp[1]);
|
|
|
|
} else {
|
|
|
|
if (index >= 8) {
|
|
|
|
bwn_shm_write_4(mac, BWN_SHARED,
|
|
|
|
BWN_SHARED_PSM + (index * 6) + 0, addrtmp[0]);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED,
|
|
|
|
BWN_SHARED_PSM + (index * 6) + 4, addrtmp[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_key_write(struct bwn_mac *mac, uint8_t index, uint8_t algorithm,
|
|
|
|
const uint8_t *key)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
uint32_t offset;
|
|
|
|
uint16_t kidx, value;
|
|
|
|
|
|
|
|
kidx = BWN_SEC_KEY2FW(mac, index);
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED,
|
|
|
|
BWN_SHARED_KEYIDX_BLOCK + (kidx * 2), (kidx << 4) | algorithm);
|
|
|
|
|
|
|
|
offset = mac->mac_ktp + (index * BWN_SEC_KEYSIZE);
|
|
|
|
for (i = 0; i < BWN_SEC_KEYSIZE; i += 2) {
|
|
|
|
value = key[i];
|
|
|
|
value |= (uint16_t)(key[i + 1]) << 8;
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, offset + i, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_phy_exit(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
|
|
|
|
mac->mac_phy.rf_onoff(mac, 0);
|
|
|
|
if (mac->mac_phy.exit != NULL)
|
|
|
|
mac->mac_phy.exit(mac);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_dma_free(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_dma *dma;
|
|
|
|
|
|
|
|
if ((mac->mac_flags & BWN_MAC_FLAG_DMA) == 0)
|
|
|
|
return;
|
|
|
|
dma = &mac->mac_method.dma;
|
|
|
|
|
|
|
|
bwn_dma_ringfree(&dma->rx);
|
|
|
|
bwn_dma_ringfree(&dma->wme[WME_AC_BK]);
|
|
|
|
bwn_dma_ringfree(&dma->wme[WME_AC_BE]);
|
|
|
|
bwn_dma_ringfree(&dma->wme[WME_AC_VI]);
|
|
|
|
bwn_dma_ringfree(&dma->wme[WME_AC_VO]);
|
|
|
|
bwn_dma_ringfree(&dma->mcast);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_core_stop(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
|
|
|
|
if (mac->mac_status < BWN_MAC_STATUS_STARTED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
callout_stop(&sc->sc_rfswitch_ch);
|
|
|
|
callout_stop(&sc->sc_task_ch);
|
|
|
|
callout_stop(&sc->sc_watchdog_ch);
|
|
|
|
sc->sc_watchdog_timer = 0;
|
|
|
|
BWN_WRITE_4(mac, BWN_INTR_MASK, 0);
|
|
|
|
BWN_READ_4(mac, BWN_INTR_MASK);
|
|
|
|
bwn_mac_suspend(mac);
|
|
|
|
|
|
|
|
mac->mac_status = BWN_MAC_STATUS_INITED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_switch_band(struct bwn_softc *sc, struct ieee80211_channel *chan)
|
|
|
|
{
|
|
|
|
struct bwn_mac *up_dev = NULL;
|
|
|
|
struct bwn_mac *down_dev;
|
|
|
|
struct bwn_mac *mac;
|
|
|
|
int err, status;
|
|
|
|
uint8_t gmode;
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
|
|
|
|
TAILQ_FOREACH(mac, &sc->sc_maclist, mac_list) {
|
|
|
|
if (IEEE80211_IS_CHAN_2GHZ(chan) &&
|
|
|
|
mac->mac_phy.supports_2ghz) {
|
|
|
|
up_dev = mac;
|
|
|
|
gmode = 1;
|
|
|
|
} else if (IEEE80211_IS_CHAN_5GHZ(chan) &&
|
|
|
|
mac->mac_phy.supports_5ghz) {
|
|
|
|
up_dev = mac;
|
|
|
|
gmode = 0;
|
|
|
|
} else {
|
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
if (up_dev != NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (up_dev == NULL) {
|
|
|
|
device_printf(sc->sc_dev, "Could not find a device\n");
|
|
|
|
return (ENODEV);
|
|
|
|
}
|
|
|
|
if (up_dev == sc->sc_curmac && sc->sc_curmac->mac_phy.gmode == gmode)
|
|
|
|
return (0);
|
|
|
|
|
2016-05-14 23:42:37 +00:00
|
|
|
DPRINTF(sc, BWN_DEBUG_RF | BWN_DEBUG_PHY | BWN_DEBUG_RESET,
|
|
|
|
"switching to %s-GHz band\n",
|
2010-02-16 01:44:14 +00:00
|
|
|
IEEE80211_IS_CHAN_2GHZ(chan) ? "2" : "5");
|
|
|
|
|
2010-12-06 10:24:06 +00:00
|
|
|
down_dev = sc->sc_curmac;
|
2010-02-16 01:44:14 +00:00
|
|
|
status = down_dev->mac_status;
|
|
|
|
if (status >= BWN_MAC_STATUS_STARTED)
|
|
|
|
bwn_core_stop(down_dev);
|
|
|
|
if (status >= BWN_MAC_STATUS_INITED)
|
|
|
|
bwn_core_exit(down_dev);
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (down_dev != up_dev) {
|
|
|
|
err = bwn_phy_reset(down_dev);
|
|
|
|
if (err)
|
|
|
|
goto fail;
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
up_dev->mac_phy.gmode = gmode;
|
|
|
|
if (status >= BWN_MAC_STATUS_INITED) {
|
|
|
|
err = bwn_core_init(up_dev);
|
|
|
|
if (err) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"fatal: failed to initialize for %s-GHz\n",
|
|
|
|
IEEE80211_IS_CHAN_2GHZ(chan) ? "2" : "5");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (status >= BWN_MAC_STATUS_STARTED)
|
|
|
|
bwn_core_start(up_dev);
|
|
|
|
KASSERT(up_dev->mac_status == status, ("%s: fail", __func__));
|
|
|
|
sc->sc_curmac = up_dev;
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
fail:
|
|
|
|
sc->sc_curmac = NULL;
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_rf_turnon(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
|
2016-05-14 23:42:37 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: called\n", __func__);
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_mac_suspend(mac);
|
|
|
|
mac->mac_phy.rf_onoff(mac, 1);
|
|
|
|
mac->mac_phy.rf_on = 1;
|
|
|
|
bwn_mac_enable(mac);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_rf_turnoff(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
|
2016-05-14 23:42:37 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: called\n", __func__);
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_mac_suspend(mac);
|
|
|
|
mac->mac_phy.rf_onoff(mac, 0);
|
|
|
|
mac->mac_phy.rf_on = 0;
|
|
|
|
bwn_mac_enable(mac);
|
|
|
|
}
|
|
|
|
|
2016-05-14 23:20:46 +00:00
|
|
|
/*
|
Introduce bwn(4) support for the bhnd(4) bus.
Currently, bwn(4) relies on the siba_bwn(4) bus driver to provide support
for the on-chip SSB interconnect found in Broadcom's older PCI(e) Wi-Fi
adapters. Non-PCI Wi-Fi adapters, as well as the newer BCMA interconnect
found in post-2009 Broadcom Wi-Fi hardware, are not supported by
siba_bwn(4).
The bhnd(4) bus driver (also used by the FreeBSD/MIPS Broadcom port)
provides a unified kernel interface to a superset of the hardware supported
by siba_bwn; by attaching bwn(4) via bhnd(4), we can support both modern
PCI(e) Wi-Fi devices based on the BCMA backplane interconnect, as well as
Broadcom MIPS WiSoCs that include a D11 MAC core directly attached to their
SSB or BCMA backplane.
This diff introduces opt-in bwn(4) support for bhnd(4) by providing:
- A small bwn(4) driver subclass, if_bwn_bhnd, that attaches via
bhnd(4) instead of siba_bwn(4).
- A bhndb(4)-based PCI host bridge driver, if_bwn_pci, that optionally
probes at a higher priority than the siba_bwn(4) PCI driver.
- A set of compatibility shims that perform translation of bwn(4)'s
siba_bwn function calls into their bhnd(9) API equivalents when bwn(4)
is attached via a bhnd(4) bus parent. When bwn(4) is attached via
siba_bwn(4), all siba_bwn function calls are simply passed through to
their original implementations.
To test bwn(4) with bhnd(4), place the following lines in loader.conf(5):
hw.bwn_pci.preferred="1"
if_bwn_pci_load="YES
bwn_v4_ucode_load="YES"
bwn_v4_lp_ucode_load="YES"
To verify that bwn(4) is using bhnd(4), you can check dmesg:
bwn0: <Broadcom 802.11 MAC/PHY/Radio, rev 15> ... on bhnd0
... or devinfo(8):
pcib2
pci2
bwn_pci0
bhndb0
bhnd0
bwn0
...
bwn(4)/bhnd(4) has been tested for regressions with most chipsets currently
supported by bwn(4), including:
- BCM4312
- BCM4318
- BCM4321
With minimal changes to the DMA code (not included in this commit), I was
also able to test support for newer BCMA devices by bringing up basic
working Wi-Fi on two previously unsupported, BCMA-based N-PHY chipsets:
- BCM43224
- BCM43225
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation & Plausible Labs
Differential Revision: https://reviews.freebsd.org/D13041
2017-12-02 02:21:27 +00:00
|
|
|
* PHY reset.
|
2016-05-14 23:20:46 +00:00
|
|
|
*/
|
2018-02-05 23:38:15 +00:00
|
|
|
static int
|
Introduce bwn(4) support for the bhnd(4) bus.
Currently, bwn(4) relies on the siba_bwn(4) bus driver to provide support
for the on-chip SSB interconnect found in Broadcom's older PCI(e) Wi-Fi
adapters. Non-PCI Wi-Fi adapters, as well as the newer BCMA interconnect
found in post-2009 Broadcom Wi-Fi hardware, are not supported by
siba_bwn(4).
The bhnd(4) bus driver (also used by the FreeBSD/MIPS Broadcom port)
provides a unified kernel interface to a superset of the hardware supported
by siba_bwn; by attaching bwn(4) via bhnd(4), we can support both modern
PCI(e) Wi-Fi devices based on the BCMA backplane interconnect, as well as
Broadcom MIPS WiSoCs that include a D11 MAC core directly attached to their
SSB or BCMA backplane.
This diff introduces opt-in bwn(4) support for bhnd(4) by providing:
- A small bwn(4) driver subclass, if_bwn_bhnd, that attaches via
bhnd(4) instead of siba_bwn(4).
- A bhndb(4)-based PCI host bridge driver, if_bwn_pci, that optionally
probes at a higher priority than the siba_bwn(4) PCI driver.
- A set of compatibility shims that perform translation of bwn(4)'s
siba_bwn function calls into their bhnd(9) API equivalents when bwn(4)
is attached via a bhnd(4) bus parent. When bwn(4) is attached via
siba_bwn(4), all siba_bwn function calls are simply passed through to
their original implementations.
To test bwn(4) with bhnd(4), place the following lines in loader.conf(5):
hw.bwn_pci.preferred="1"
if_bwn_pci_load="YES
bwn_v4_ucode_load="YES"
bwn_v4_lp_ucode_load="YES"
To verify that bwn(4) is using bhnd(4), you can check dmesg:
bwn0: <Broadcom 802.11 MAC/PHY/Radio, rev 15> ... on bhnd0
... or devinfo(8):
pcib2
pci2
bwn_pci0
bhndb0
bhnd0
bwn0
...
bwn(4)/bhnd(4) has been tested for regressions with most chipsets currently
supported by bwn(4), including:
- BCM4312
- BCM4318
- BCM4321
With minimal changes to the DMA code (not included in this commit), I was
also able to test support for newer BCMA devices by bringing up basic
working Wi-Fi on two previously unsupported, BCMA-based N-PHY chipsets:
- BCM43224
- BCM43225
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation & Plausible Labs
Differential Revision: https://reviews.freebsd.org/D13041
2017-12-02 02:21:27 +00:00
|
|
|
bwn_phy_reset(struct bwn_mac *mac)
|
2010-02-16 01:44:14 +00:00
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
struct bwn_softc *sc;
|
|
|
|
uint16_t iost, mask;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
sc = mac->mac_sc;
|
|
|
|
|
|
|
|
iost = BWN_IOCTL_PHYRESET | BHND_IOCTL_CLK_FORCE;
|
|
|
|
mask = iost | BWN_IOCTL_SUPPORT_G;
|
|
|
|
|
|
|
|
if ((error = bhnd_write_ioctl(sc->sc_dev, iost, mask)))
|
|
|
|
return (error);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
DELAY(1000);
|
2018-02-05 23:38:15 +00:00
|
|
|
|
|
|
|
iost &= ~BHND_IOCTL_CLK_FORCE;
|
|
|
|
|
|
|
|
if ((error = bhnd_write_ioctl(sc->sc_dev, iost, mask)))
|
|
|
|
return (error);
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
DELAY(1000);
|
2018-02-05 23:38:15 +00:00
|
|
|
|
|
|
|
return (0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
|
|
|
|
{
|
|
|
|
struct bwn_vap *bvp = BWN_VAP(vap);
|
|
|
|
struct ieee80211com *ic= vap->iv_ic;
|
|
|
|
enum ieee80211_state ostate = vap->iv_state;
|
2015-08-17 23:44:38 +00:00
|
|
|
struct bwn_softc *sc = ic->ic_softc;
|
2010-02-16 01:44:14 +00:00
|
|
|
struct bwn_mac *mac = sc->sc_curmac;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
DPRINTF(sc, BWN_DEBUG_STATE, "%s: %s -> %s\n", __func__,
|
|
|
|
ieee80211_state_name[vap->iv_state],
|
|
|
|
ieee80211_state_name[nstate]);
|
|
|
|
|
|
|
|
error = bvp->bv_newstate(vap, nstate, arg);
|
|
|
|
if (error != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
BWN_LOCK(sc);
|
|
|
|
|
|
|
|
bwn_led_newstate(mac, nstate);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the BSSID when we stop a STA
|
|
|
|
*/
|
|
|
|
if (vap->iv_opmode == IEEE80211_M_STA) {
|
|
|
|
if (ostate == IEEE80211_S_RUN && nstate != IEEE80211_S_RUN) {
|
|
|
|
/*
|
|
|
|
* Clear out the BSSID. If we reassociate to
|
|
|
|
* the same AP, this will reinialize things
|
|
|
|
* correctly...
|
|
|
|
*/
|
|
|
|
if (ic->ic_opmode == IEEE80211_M_STA &&
|
|
|
|
(sc->sc_flags & BWN_FLAG_INVALID) == 0) {
|
|
|
|
memset(sc->sc_bssid, 0, IEEE80211_ADDR_LEN);
|
|
|
|
bwn_set_macaddr(mac);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-27 23:04:29 +00:00
|
|
|
if (vap->iv_opmode == IEEE80211_M_MONITOR ||
|
|
|
|
vap->iv_opmode == IEEE80211_M_AHDEMO) {
|
2010-02-16 01:44:14 +00:00
|
|
|
/* XXX nothing to do? */
|
|
|
|
} else if (nstate == IEEE80211_S_RUN) {
|
|
|
|
memcpy(sc->sc_bssid, vap->iv_bss->ni_bssid, IEEE80211_ADDR_LEN);
|
|
|
|
bwn_set_opmode(mac);
|
|
|
|
bwn_set_pretbtt(mac);
|
|
|
|
bwn_spu_setdelay(mac, 0);
|
|
|
|
bwn_set_macaddr(mac);
|
|
|
|
}
|
|
|
|
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_set_pretbtt(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2010-02-16 01:44:14 +00:00
|
|
|
uint16_t pretbtt;
|
|
|
|
|
|
|
|
if (ic->ic_opmode == IEEE80211_M_IBSS)
|
|
|
|
pretbtt = 2;
|
|
|
|
else
|
|
|
|
pretbtt = (mac->mac_phy.type == BWN_PHYTYPE_A) ? 120 : 250;
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PRETBTT, pretbtt);
|
|
|
|
BWN_WRITE_2(mac, BWN_TSF_CFP_PRETBTT, pretbtt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_intr(void *arg)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = arg;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint32_t reason;
|
|
|
|
|
2010-03-09 19:58:00 +00:00
|
|
|
if (mac->mac_status < BWN_MAC_STATUS_STARTED ||
|
|
|
|
(sc->sc_flags & BWN_FLAG_INVALID))
|
2010-02-16 01:44:14 +00:00
|
|
|
return (FILTER_STRAY);
|
|
|
|
|
2016-05-19 04:29:25 +00:00
|
|
|
DPRINTF(sc, BWN_DEBUG_INTR, "%s: called\n", __func__);
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
reason = BWN_READ_4(mac, BWN_INTR_REASON);
|
|
|
|
if (reason == 0xffffffff) /* shared IRQ */
|
|
|
|
return (FILTER_STRAY);
|
|
|
|
reason &= mac->mac_intr_mask;
|
|
|
|
if (reason == 0)
|
|
|
|
return (FILTER_HANDLED);
|
2016-05-19 04:29:25 +00:00
|
|
|
DPRINTF(sc, BWN_DEBUG_INTR, "%s: reason=0x%08x\n", __func__, reason);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
mac->mac_reason[0] = BWN_READ_4(mac, BWN_DMA0_REASON) & 0x0001dc00;
|
|
|
|
mac->mac_reason[1] = BWN_READ_4(mac, BWN_DMA1_REASON) & 0x0000dc00;
|
|
|
|
mac->mac_reason[2] = BWN_READ_4(mac, BWN_DMA2_REASON) & 0x0000dc00;
|
|
|
|
mac->mac_reason[3] = BWN_READ_4(mac, BWN_DMA3_REASON) & 0x0001dc00;
|
|
|
|
mac->mac_reason[4] = BWN_READ_4(mac, BWN_DMA4_REASON) & 0x0000dc00;
|
|
|
|
BWN_WRITE_4(mac, BWN_INTR_REASON, reason);
|
|
|
|
BWN_WRITE_4(mac, BWN_DMA0_REASON, mac->mac_reason[0]);
|
|
|
|
BWN_WRITE_4(mac, BWN_DMA1_REASON, mac->mac_reason[1]);
|
|
|
|
BWN_WRITE_4(mac, BWN_DMA2_REASON, mac->mac_reason[2]);
|
|
|
|
BWN_WRITE_4(mac, BWN_DMA3_REASON, mac->mac_reason[3]);
|
|
|
|
BWN_WRITE_4(mac, BWN_DMA4_REASON, mac->mac_reason[4]);
|
|
|
|
|
|
|
|
/* Disable interrupts. */
|
|
|
|
BWN_WRITE_4(mac, BWN_INTR_MASK, 0);
|
|
|
|
|
|
|
|
mac->mac_reason_intr = reason;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
BWN_BARRIER(mac, 0, 0, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2016-03-01 17:47:32 +00:00
|
|
|
taskqueue_enqueue(sc->sc_tq, &mac->mac_intrtask);
|
2010-02-16 01:44:14 +00:00
|
|
|
return (FILTER_HANDLED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_intrtask(void *arg, int npending)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = arg;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint32_t merged = 0;
|
|
|
|
int i, tx = 0, rx = 0;
|
|
|
|
|
|
|
|
BWN_LOCK(sc);
|
2010-03-09 19:58:00 +00:00
|
|
|
if (mac->mac_status < BWN_MAC_STATUS_STARTED ||
|
|
|
|
(sc->sc_flags & BWN_FLAG_INVALID)) {
|
2010-02-16 01:44:14 +00:00
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < N(mac->mac_reason); i++)
|
|
|
|
merged |= mac->mac_reason[i];
|
|
|
|
|
|
|
|
if (mac->mac_reason_intr & BWN_INTR_MAC_TXERR)
|
|
|
|
device_printf(sc->sc_dev, "MAC trans error\n");
|
|
|
|
|
|
|
|
if (mac->mac_reason_intr & BWN_INTR_PHY_TXERR) {
|
|
|
|
DPRINTF(sc, BWN_DEBUG_INTR, "%s: PHY trans error\n", __func__);
|
|
|
|
mac->mac_phy.txerrors--;
|
|
|
|
if (mac->mac_phy.txerrors == 0) {
|
|
|
|
mac->mac_phy.txerrors = BWN_TXERROR_MAX;
|
|
|
|
bwn_restart(mac, "PHY TX errors");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (merged & (BWN_DMAINTR_FATALMASK | BWN_DMAINTR_NONFATALMASK)) {
|
|
|
|
if (merged & BWN_DMAINTR_FATALMASK) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"Fatal DMA error: %#x %#x %#x %#x %#x %#x\n",
|
|
|
|
mac->mac_reason[0], mac->mac_reason[1],
|
|
|
|
mac->mac_reason[2], mac->mac_reason[3],
|
|
|
|
mac->mac_reason[4], mac->mac_reason[5]);
|
|
|
|
bwn_restart(mac, "DMA error");
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (merged & BWN_DMAINTR_NONFATALMASK) {
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"DMA error: %#x %#x %#x %#x %#x %#x\n",
|
|
|
|
mac->mac_reason[0], mac->mac_reason[1],
|
|
|
|
mac->mac_reason[2], mac->mac_reason[3],
|
|
|
|
mac->mac_reason[4], mac->mac_reason[5]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mac->mac_reason_intr & BWN_INTR_UCODE_DEBUG)
|
|
|
|
bwn_intr_ucode_debug(mac);
|
|
|
|
if (mac->mac_reason_intr & BWN_INTR_TBTT_INDI)
|
|
|
|
bwn_intr_tbtt_indication(mac);
|
|
|
|
if (mac->mac_reason_intr & BWN_INTR_ATIM_END)
|
|
|
|
bwn_intr_atim_end(mac);
|
|
|
|
if (mac->mac_reason_intr & BWN_INTR_BEACON)
|
|
|
|
bwn_intr_beacon(mac);
|
|
|
|
if (mac->mac_reason_intr & BWN_INTR_PMQ)
|
|
|
|
bwn_intr_pmq(mac);
|
|
|
|
if (mac->mac_reason_intr & BWN_INTR_NOISESAMPLE_OK)
|
|
|
|
bwn_intr_noise(mac);
|
|
|
|
|
|
|
|
if (mac->mac_flags & BWN_MAC_FLAG_DMA) {
|
|
|
|
if (mac->mac_reason[0] & BWN_DMAINTR_RX_DONE) {
|
|
|
|
bwn_dma_rx(mac->mac_method.dma.rx);
|
|
|
|
rx = 1;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
rx = bwn_pio_rx(&mac->mac_method.pio.rx);
|
|
|
|
|
|
|
|
KASSERT(!(mac->mac_reason[1] & BWN_DMAINTR_RX_DONE), ("%s", __func__));
|
|
|
|
KASSERT(!(mac->mac_reason[2] & BWN_DMAINTR_RX_DONE), ("%s", __func__));
|
|
|
|
KASSERT(!(mac->mac_reason[3] & BWN_DMAINTR_RX_DONE), ("%s", __func__));
|
|
|
|
KASSERT(!(mac->mac_reason[4] & BWN_DMAINTR_RX_DONE), ("%s", __func__));
|
|
|
|
KASSERT(!(mac->mac_reason[5] & BWN_DMAINTR_RX_DONE), ("%s", __func__));
|
|
|
|
|
|
|
|
if (mac->mac_reason_intr & BWN_INTR_TX_OK) {
|
|
|
|
bwn_intr_txeof(mac);
|
|
|
|
tx = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
BWN_WRITE_4(mac, BWN_INTR_MASK, mac->mac_intr_mask);
|
|
|
|
|
|
|
|
if (sc->sc_blink_led != NULL && sc->sc_led_blink) {
|
|
|
|
int evt = BWN_LED_EVENT_NONE;
|
|
|
|
|
|
|
|
if (tx && rx) {
|
|
|
|
if (sc->sc_rx_rate > sc->sc_tx_rate)
|
|
|
|
evt = BWN_LED_EVENT_RX;
|
|
|
|
else
|
|
|
|
evt = BWN_LED_EVENT_TX;
|
|
|
|
} else if (tx) {
|
|
|
|
evt = BWN_LED_EVENT_TX;
|
|
|
|
} else if (rx) {
|
|
|
|
evt = BWN_LED_EVENT_RX;
|
|
|
|
} else if (rx == 0) {
|
|
|
|
evt = BWN_LED_EVENT_POLL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (evt != BWN_LED_EVENT_NONE)
|
|
|
|
bwn_led_event(mac, evt);
|
|
|
|
}
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
if (mbufq_first(&sc->sc_snd) != NULL)
|
|
|
|
bwn_start(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
BWN_BARRIER(mac, 0, 0, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_restart(struct bwn_mac *mac, const char *msg)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
if (mac->mac_status < BWN_MAC_STATUS_INITED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
device_printf(sc->sc_dev, "HW reset: %s\n", msg);
|
|
|
|
ieee80211_runtask(ic, &mac->mac_hwreset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_intr_ucode_debug(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint16_t reason;
|
|
|
|
|
|
|
|
if (mac->mac_fw.opensource == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
reason = bwn_shm_read_2(mac, BWN_SCRATCH, BWN_DEBUGINTR_REASON_REG);
|
|
|
|
switch (reason) {
|
|
|
|
case BWN_DEBUGINTR_PANIC:
|
|
|
|
bwn_handle_fwpanic(mac);
|
|
|
|
break;
|
|
|
|
case BWN_DEBUGINTR_DUMP_SHM:
|
|
|
|
device_printf(sc->sc_dev, "BWN_DEBUGINTR_DUMP_SHM\n");
|
|
|
|
break;
|
|
|
|
case BWN_DEBUGINTR_DUMP_REGS:
|
|
|
|
device_printf(sc->sc_dev, "BWN_DEBUGINTR_DUMP_REGS\n");
|
|
|
|
break;
|
|
|
|
case BWN_DEBUGINTR_MARKER:
|
|
|
|
device_printf(sc->sc_dev, "BWN_DEBUGINTR_MARKER\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"ucode debug unknown reason: %#x\n", reason);
|
|
|
|
}
|
|
|
|
|
|
|
|
bwn_shm_write_2(mac, BWN_SCRATCH, BWN_DEBUGINTR_REASON_REG,
|
|
|
|
BWN_DEBUGINTR_ACK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_intr_tbtt_indication(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
if (ic->ic_opmode != IEEE80211_M_HOSTAP)
|
|
|
|
bwn_psctl(mac, 0);
|
|
|
|
if (ic->ic_opmode == IEEE80211_M_IBSS)
|
|
|
|
mac->mac_flags |= BWN_MAC_FLAG_DFQVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_intr_atim_end(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (mac->mac_flags & BWN_MAC_FLAG_DFQVALID) {
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCMD,
|
|
|
|
BWN_READ_4(mac, BWN_MACCMD) | BWN_MACCMD_DFQ_VALID);
|
|
|
|
mac->mac_flags &= ~BWN_MAC_FLAG_DFQVALID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_intr_beacon(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2010-02-16 01:44:14 +00:00
|
|
|
uint32_t cmd, beacon0, beacon1;
|
|
|
|
|
|
|
|
if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
|
|
|
|
ic->ic_opmode == IEEE80211_M_MBSS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mac->mac_intr_mask &= ~BWN_INTR_BEACON;
|
|
|
|
|
|
|
|
cmd = BWN_READ_4(mac, BWN_MACCMD);
|
|
|
|
beacon0 = (cmd & BWN_MACCMD_BEACON0_VALID);
|
|
|
|
beacon1 = (cmd & BWN_MACCMD_BEACON1_VALID);
|
|
|
|
|
|
|
|
if (beacon0 && beacon1) {
|
|
|
|
BWN_WRITE_4(mac, BWN_INTR_REASON, BWN_INTR_BEACON);
|
|
|
|
mac->mac_intr_mask |= BWN_INTR_BEACON;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_flags & BWN_FLAG_NEED_BEACON_TP) {
|
|
|
|
sc->sc_flags &= ~BWN_FLAG_NEED_BEACON_TP;
|
|
|
|
bwn_load_beacon0(mac);
|
|
|
|
bwn_load_beacon1(mac);
|
|
|
|
cmd = BWN_READ_4(mac, BWN_MACCMD);
|
|
|
|
cmd |= BWN_MACCMD_BEACON0_VALID;
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCMD, cmd);
|
|
|
|
} else {
|
|
|
|
if (!beacon0) {
|
|
|
|
bwn_load_beacon0(mac);
|
|
|
|
cmd = BWN_READ_4(mac, BWN_MACCMD);
|
|
|
|
cmd |= BWN_MACCMD_BEACON0_VALID;
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCMD, cmd);
|
|
|
|
} else if (!beacon1) {
|
|
|
|
bwn_load_beacon1(mac);
|
|
|
|
cmd = BWN_READ_4(mac, BWN_MACCMD);
|
|
|
|
cmd |= BWN_MACCMD_BEACON1_VALID;
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCMD, cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_intr_pmq(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
uint32_t tmp;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
tmp = BWN_READ_4(mac, BWN_PS_STATUS);
|
|
|
|
if (!(tmp & 0x00000008))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
BWN_WRITE_2(mac, BWN_PS_STATUS, 0x0002);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_intr_noise(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_phy_g *pg = &mac->mac_phy.phy_g;
|
|
|
|
uint16_t tmp;
|
|
|
|
uint8_t noise[4];
|
|
|
|
uint8_t i, j;
|
|
|
|
int32_t average;
|
|
|
|
|
|
|
|
if (mac->mac_phy.type != BWN_PHYTYPE_G)
|
|
|
|
return;
|
|
|
|
|
|
|
|
KASSERT(mac->mac_noise.noi_running, ("%s: fail", __func__));
|
|
|
|
*((uint32_t *)noise) = htole32(bwn_jssi_read(mac));
|
|
|
|
if (noise[0] == 0x7f || noise[1] == 0x7f || noise[2] == 0x7f ||
|
|
|
|
noise[3] == 0x7f)
|
|
|
|
goto new;
|
|
|
|
|
|
|
|
KASSERT(mac->mac_noise.noi_nsamples < 8,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
i = mac->mac_noise.noi_nsamples;
|
|
|
|
noise[0] = MIN(MAX(noise[0], 0), N(pg->pg_nrssi_lt) - 1);
|
|
|
|
noise[1] = MIN(MAX(noise[1], 0), N(pg->pg_nrssi_lt) - 1);
|
|
|
|
noise[2] = MIN(MAX(noise[2], 0), N(pg->pg_nrssi_lt) - 1);
|
|
|
|
noise[3] = MIN(MAX(noise[3], 0), N(pg->pg_nrssi_lt) - 1);
|
|
|
|
mac->mac_noise.noi_samples[i][0] = pg->pg_nrssi_lt[noise[0]];
|
|
|
|
mac->mac_noise.noi_samples[i][1] = pg->pg_nrssi_lt[noise[1]];
|
|
|
|
mac->mac_noise.noi_samples[i][2] = pg->pg_nrssi_lt[noise[2]];
|
|
|
|
mac->mac_noise.noi_samples[i][3] = pg->pg_nrssi_lt[noise[3]];
|
|
|
|
mac->mac_noise.noi_nsamples++;
|
|
|
|
if (mac->mac_noise.noi_nsamples == 8) {
|
|
|
|
average = 0;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
|
|
for (j = 0; j < 4; j++)
|
|
|
|
average += mac->mac_noise.noi_samples[i][j];
|
|
|
|
}
|
|
|
|
average = (((average / 32) * 125) + 64) / 128;
|
|
|
|
tmp = (bwn_shm_read_2(mac, BWN_SHARED, 0x40c) / 128) & 0x1f;
|
|
|
|
if (tmp >= 8)
|
|
|
|
average += 2;
|
|
|
|
else
|
|
|
|
average -= 25;
|
|
|
|
average -= (tmp == 8) ? 72 : 48;
|
|
|
|
|
|
|
|
mac->mac_stats.link_noise = average;
|
|
|
|
mac->mac_noise.noi_running = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
new:
|
|
|
|
bwn_noise_gensample(mac);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_pio_rx(struct bwn_pio_rxqueue *prq)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = prq->prq_mac;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
|
|
|
|
if (mac->mac_status < BWN_MAC_STATUS_STARTED)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
for (i = 0; i < 5000; i++) {
|
|
|
|
if (bwn_pio_rxeof(prq) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i >= 5000)
|
|
|
|
device_printf(sc->sc_dev, "too many RX frames in PIO mode\n");
|
|
|
|
return ((i > 0) ? 1 : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_dma_rx(struct bwn_dma_ring *dr)
|
|
|
|
{
|
|
|
|
int slot, curslot;
|
|
|
|
|
|
|
|
KASSERT(!dr->dr_tx, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
curslot = dr->get_curslot(dr);
|
|
|
|
KASSERT(curslot >= 0 && curslot < dr->dr_numslots,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
|
|
|
slot = dr->dr_curslot;
|
|
|
|
for (; slot != curslot; slot = bwn_dma_nextslot(dr, slot))
|
|
|
|
bwn_dma_rxeof(dr, &slot);
|
|
|
|
|
|
|
|
bus_dmamap_sync(dr->dr_ring_dtag, dr->dr_ring_dmap,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
dr->set_curslot(dr, slot);
|
|
|
|
dr->dr_curslot = slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_intr_txeof(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_txstatus stat;
|
|
|
|
uint32_t stat0, stat1;
|
|
|
|
uint16_t tmp;
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(mac->mac_sc);
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
stat0 = BWN_READ_4(mac, BWN_XMITSTAT_0);
|
|
|
|
if (!(stat0 & 0x00000001))
|
|
|
|
break;
|
|
|
|
stat1 = BWN_READ_4(mac, BWN_XMITSTAT_1);
|
|
|
|
|
2016-05-04 02:11:45 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_XMIT,
|
|
|
|
"%s: stat0=0x%08x, stat1=0x%08x\n",
|
|
|
|
__func__,
|
|
|
|
stat0,
|
|
|
|
stat1);
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
stat.cookie = (stat0 >> 16);
|
|
|
|
stat.seq = (stat1 & 0x0000ffff);
|
|
|
|
stat.phy_stat = ((stat1 & 0x00ff0000) >> 16);
|
|
|
|
tmp = (stat0 & 0x0000ffff);
|
|
|
|
stat.framecnt = ((tmp & 0xf000) >> 12);
|
|
|
|
stat.rtscnt = ((tmp & 0x0f00) >> 8);
|
|
|
|
stat.sreason = ((tmp & 0x001c) >> 2);
|
|
|
|
stat.pm = (tmp & 0x0080) ? 1 : 0;
|
|
|
|
stat.im = (tmp & 0x0040) ? 1 : 0;
|
|
|
|
stat.ampdu = (tmp & 0x0020) ? 1 : 0;
|
|
|
|
stat.ack = (tmp & 0x0002) ? 1 : 0;
|
|
|
|
|
2016-05-14 23:27:55 +00:00
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_XMIT,
|
|
|
|
"%s: cookie=%d, seq=%d, phystat=0x%02x, framecnt=%d, "
|
|
|
|
"rtscnt=%d, sreason=%d, pm=%d, im=%d, ampdu=%d, ack=%d\n",
|
|
|
|
__func__,
|
|
|
|
stat.cookie,
|
|
|
|
stat.seq,
|
|
|
|
stat.phy_stat,
|
|
|
|
stat.framecnt,
|
|
|
|
stat.rtscnt,
|
|
|
|
stat.sreason,
|
|
|
|
stat.pm,
|
|
|
|
stat.im,
|
|
|
|
stat.ampdu,
|
|
|
|
stat.ack);
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_handle_txeof(mac, &stat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_hwreset(void *arg, int npending)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = arg;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
int error = 0;
|
|
|
|
int prev_status;
|
|
|
|
|
|
|
|
BWN_LOCK(sc);
|
|
|
|
|
|
|
|
prev_status = mac->mac_status;
|
|
|
|
if (prev_status >= BWN_MAC_STATUS_STARTED)
|
|
|
|
bwn_core_stop(mac);
|
|
|
|
if (prev_status >= BWN_MAC_STATUS_INITED)
|
|
|
|
bwn_core_exit(mac);
|
|
|
|
|
|
|
|
if (prev_status >= BWN_MAC_STATUS_INITED) {
|
|
|
|
error = bwn_core_init(mac);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (prev_status >= BWN_MAC_STATUS_STARTED)
|
|
|
|
bwn_core_start(mac);
|
|
|
|
out:
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "%s: failed (%d)\n", __func__, error);
|
|
|
|
sc->sc_curmac = NULL;
|
|
|
|
}
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_handle_fwpanic(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint16_t reason;
|
|
|
|
|
|
|
|
reason = bwn_shm_read_2(mac, BWN_SCRATCH, BWN_FWPANIC_REASON_REG);
|
|
|
|
device_printf(sc->sc_dev,"fw panic (%u)\n", reason);
|
|
|
|
|
|
|
|
if (reason == BWN_FWPANIC_RESTART)
|
|
|
|
bwn_restart(mac, "ucode panic");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_load_beacon0(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
|
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_load_beacon1(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
|
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
bwn_jssi_read(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
uint32_t val = 0;
|
|
|
|
|
|
|
|
val = bwn_shm_read_2(mac, BWN_SHARED, 0x08a);
|
|
|
|
val <<= 16;
|
|
|
|
val |= bwn_shm_read_2(mac, BWN_SHARED, 0x088);
|
|
|
|
|
|
|
|
return (val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_noise_gensample(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
uint32_t jssi = 0x7f7f7f7f;
|
|
|
|
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, 0x088, (jssi & 0x0000ffff));
|
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, 0x08a, (jssi & 0xffff0000) >> 16);
|
|
|
|
BWN_WRITE_4(mac, BWN_MACCMD,
|
|
|
|
BWN_READ_4(mac, BWN_MACCMD) | BWN_MACCMD_BGNOISE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_dma_freeslot(struct bwn_dma_ring *dr)
|
|
|
|
{
|
2010-02-23 05:02:10 +00:00
|
|
|
BWN_ASSERT_LOCKED(dr->dr_mac->mac_sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
return (dr->dr_numslots - dr->dr_usedslot);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_dma_nextslot(struct bwn_dma_ring *dr, int slot)
|
|
|
|
{
|
2010-02-23 05:02:10 +00:00
|
|
|
BWN_ASSERT_LOCKED(dr->dr_mac->mac_sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
KASSERT(slot >= -1 && slot <= dr->dr_numslots - 1,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
if (slot == dr->dr_numslots - 1)
|
|
|
|
return (0);
|
|
|
|
return (slot + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_dma_rxeof(struct bwn_dma_ring *dr, int *slot)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = dr->dr_mac;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
struct bwn_dma *dma = &mac->mac_method.dma;
|
|
|
|
struct bwn_dmadesc_generic *desc;
|
|
|
|
struct bwn_dmadesc_meta *meta;
|
|
|
|
struct bwn_rxhdr4 *rxhdr;
|
|
|
|
struct mbuf *m;
|
|
|
|
uint32_t macstat;
|
|
|
|
int32_t tmp;
|
|
|
|
int cnt = 0;
|
|
|
|
uint16_t len;
|
|
|
|
|
|
|
|
dr->getdesc(dr, *slot, &desc, &meta);
|
|
|
|
|
|
|
|
bus_dmamap_sync(dma->rxbuf_dtag, meta->mt_dmap, BUS_DMASYNC_POSTREAD);
|
|
|
|
m = meta->mt_m;
|
|
|
|
|
|
|
|
if (bwn_dma_newbuf(dr, desc, meta, 0)) {
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
counter_u64_add(sc->sc_ic.ic_ierrors, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rxhdr = mtod(m, struct bwn_rxhdr4 *);
|
|
|
|
len = le16toh(rxhdr->frame_len);
|
|
|
|
if (len <= 0) {
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
counter_u64_add(sc->sc_ic.ic_ierrors, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (bwn_dma_check_redzone(dr, m)) {
|
|
|
|
device_printf(sc->sc_dev, "redzone error.\n");
|
|
|
|
bwn_dma_set_redzone(dr, m);
|
|
|
|
bus_dmamap_sync(dma->rxbuf_dtag, meta->mt_dmap,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (len > dr->dr_rx_bufsize) {
|
|
|
|
tmp = len;
|
|
|
|
while (1) {
|
|
|
|
dr->getdesc(dr, *slot, &desc, &meta);
|
|
|
|
bwn_dma_set_redzone(dr, meta->mt_m);
|
|
|
|
bus_dmamap_sync(dma->rxbuf_dtag, meta->mt_dmap,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
*slot = bwn_dma_nextslot(dr, *slot);
|
|
|
|
cnt++;
|
|
|
|
tmp -= dr->dr_rx_bufsize;
|
|
|
|
if (tmp <= 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
device_printf(sc->sc_dev, "too small buffer "
|
|
|
|
"(len %u buffer %u dropped %d)\n",
|
|
|
|
len, dr->dr_rx_bufsize, cnt);
|
|
|
|
return;
|
|
|
|
}
|
2016-05-18 05:56:25 +00:00
|
|
|
|
|
|
|
switch (mac->mac_fw.fw_hdr_format) {
|
|
|
|
case BWN_FW_HDR_351:
|
|
|
|
case BWN_FW_HDR_410:
|
|
|
|
macstat = le32toh(rxhdr->ps4.r351.mac_status);
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_598:
|
|
|
|
macstat = le32toh(rxhdr->ps4.r598.mac_status);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
if (macstat & BWN_RX_MAC_FCSERR) {
|
|
|
|
if (!(mac->mac_sc->sc_filters & BWN_MACCTL_PASS_BADFCS)) {
|
|
|
|
device_printf(sc->sc_dev, "RX drop\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m->m_len = m->m_pkthdr.len = len + dr->dr_frameoffset;
|
|
|
|
m_adj(m, dr->dr_frameoffset);
|
|
|
|
|
|
|
|
bwn_rxeof(dr->dr_mac, m, rxhdr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_handle_txeof(struct bwn_mac *mac, const struct bwn_txstatus *status)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2010-02-23 19:55:54 +00:00
|
|
|
struct bwn_stats *stats = &mac->mac_stats;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(mac->mac_sc);
|
|
|
|
|
|
|
|
if (status->im)
|
|
|
|
device_printf(sc->sc_dev, "TODO: STATUS IM\n");
|
|
|
|
if (status->ampdu)
|
|
|
|
device_printf(sc->sc_dev, "TODO: STATUS AMPDU\n");
|
|
|
|
if (status->rtscnt) {
|
|
|
|
if (status->rtscnt == 0xf)
|
2010-02-23 19:55:54 +00:00
|
|
|
stats->rtsfail++;
|
2010-02-16 01:44:14 +00:00
|
|
|
else
|
2010-02-23 19:55:54 +00:00
|
|
|
stats->rts++;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mac->mac_flags & BWN_MAC_FLAG_DMA) {
|
|
|
|
bwn_dma_handle_txeof(mac, status);
|
|
|
|
} else {
|
|
|
|
bwn_pio_handle_txeof(mac, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
bwn_phy_txpower_check(mac, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
bwn_pio_rxeof(struct bwn_pio_rxqueue *prq)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = prq->prq_mac;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
struct bwn_rxhdr4 rxhdr;
|
|
|
|
struct mbuf *m;
|
|
|
|
uint32_t ctl32, macstat, v32;
|
|
|
|
unsigned int i, padding;
|
2010-07-10 21:39:03 +00:00
|
|
|
uint16_t ctl16, len, totlen, v16;
|
2010-02-16 01:44:14 +00:00
|
|
|
unsigned char *mp;
|
|
|
|
char *data;
|
|
|
|
|
|
|
|
memset(&rxhdr, 0, sizeof(rxhdr));
|
|
|
|
|
|
|
|
if (prq->prq_rev >= 8) {
|
|
|
|
ctl32 = bwn_pio_rx_read_4(prq, BWN_PIO8_RXCTL);
|
|
|
|
if (!(ctl32 & BWN_PIO8_RXCTL_FRAMEREADY))
|
|
|
|
return (0);
|
|
|
|
bwn_pio_rx_write_4(prq, BWN_PIO8_RXCTL,
|
|
|
|
BWN_PIO8_RXCTL_FRAMEREADY);
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
|
|
ctl32 = bwn_pio_rx_read_4(prq, BWN_PIO8_RXCTL);
|
|
|
|
if (ctl32 & BWN_PIO8_RXCTL_DATAREADY)
|
|
|
|
goto ready;
|
|
|
|
DELAY(10);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ctl16 = bwn_pio_rx_read_2(prq, BWN_PIO_RXCTL);
|
|
|
|
if (!(ctl16 & BWN_PIO_RXCTL_FRAMEREADY))
|
|
|
|
return (0);
|
|
|
|
bwn_pio_rx_write_2(prq, BWN_PIO_RXCTL,
|
|
|
|
BWN_PIO_RXCTL_FRAMEREADY);
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
|
|
ctl16 = bwn_pio_rx_read_2(prq, BWN_PIO_RXCTL);
|
|
|
|
if (ctl16 & BWN_PIO_RXCTL_DATAREADY)
|
|
|
|
goto ready;
|
|
|
|
DELAY(10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
device_printf(sc->sc_dev, "%s: timed out\n", __func__);
|
|
|
|
return (1);
|
|
|
|
ready:
|
2018-02-05 23:38:15 +00:00
|
|
|
if (prq->prq_rev >= 8) {
|
|
|
|
bus_read_multi_4(sc->sc_mem_res,
|
|
|
|
prq->prq_base + BWN_PIO8_RXDATA, (void *)&rxhdr,
|
|
|
|
sizeof(rxhdr));
|
|
|
|
} else {
|
|
|
|
bus_read_multi_2(sc->sc_mem_res,
|
|
|
|
prq->prq_base + BWN_PIO_RXDATA, (void *)&rxhdr,
|
|
|
|
sizeof(rxhdr));
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
len = le16toh(rxhdr.frame_len);
|
|
|
|
if (len > 0x700) {
|
|
|
|
device_printf(sc->sc_dev, "%s: len is too big\n", __func__);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (len == 0) {
|
|
|
|
device_printf(sc->sc_dev, "%s: len is 0\n", __func__);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2016-05-18 05:56:25 +00:00
|
|
|
switch (mac->mac_fw.fw_hdr_format) {
|
|
|
|
case BWN_FW_HDR_351:
|
|
|
|
case BWN_FW_HDR_410:
|
|
|
|
macstat = le32toh(rxhdr.ps4.r351.mac_status);
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_598:
|
|
|
|
macstat = le32toh(rxhdr.ps4.r598.mac_status);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
if (macstat & BWN_RX_MAC_FCSERR) {
|
|
|
|
if (!(mac->mac_sc->sc_filters & BWN_MACCTL_PASS_BADFCS)) {
|
|
|
|
device_printf(sc->sc_dev, "%s: FCS error", __func__);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
padding = (macstat & BWN_RX_MAC_PADDING) ? 2 : 0;
|
2010-07-10 21:39:03 +00:00
|
|
|
totlen = len + padding;
|
|
|
|
KASSERT(totlen <= MCLBYTES, ("too big..\n"));
|
2012-12-04 09:32:43 +00:00
|
|
|
m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (m == NULL) {
|
|
|
|
device_printf(sc->sc_dev, "%s: out of memory", __func__);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
mp = mtod(m, unsigned char *);
|
|
|
|
if (prq->prq_rev >= 8) {
|
2018-02-05 23:38:15 +00:00
|
|
|
bus_read_multi_4(sc->sc_mem_res,
|
|
|
|
prq->prq_base + BWN_PIO8_RXDATA, (void *)mp, (totlen & ~3));
|
2010-07-10 21:39:03 +00:00
|
|
|
if (totlen & 3) {
|
2010-02-16 01:44:14 +00:00
|
|
|
v32 = bwn_pio_rx_read_4(prq, BWN_PIO8_RXDATA);
|
2010-07-10 21:39:03 +00:00
|
|
|
data = &(mp[totlen - 1]);
|
|
|
|
switch (totlen & 3) {
|
2010-02-16 01:44:14 +00:00
|
|
|
case 3:
|
|
|
|
*data = (v32 >> 16);
|
|
|
|
data--;
|
|
|
|
case 2:
|
|
|
|
*data = (v32 >> 8);
|
|
|
|
data--;
|
|
|
|
case 1:
|
|
|
|
*data = v32;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2018-02-05 23:38:15 +00:00
|
|
|
bus_read_multi_2(sc->sc_mem_res,
|
|
|
|
prq->prq_base + BWN_PIO_RXDATA, (void *)mp, (totlen & ~1));
|
2010-07-10 21:39:03 +00:00
|
|
|
if (totlen & 1) {
|
2010-02-16 01:44:14 +00:00
|
|
|
v16 = bwn_pio_rx_read_2(prq, BWN_PIO_RXDATA);
|
2010-07-10 21:39:03 +00:00
|
|
|
mp[totlen - 1] = v16;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-10 21:39:03 +00:00
|
|
|
m->m_len = m->m_pkthdr.len = totlen;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
bwn_rxeof(prq->prq_mac, m, &rxhdr);
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
error:
|
|
|
|
if (prq->prq_rev >= 8)
|
|
|
|
bwn_pio_rx_write_4(prq, BWN_PIO8_RXCTL,
|
|
|
|
BWN_PIO8_RXCTL_DATAREADY);
|
|
|
|
else
|
|
|
|
bwn_pio_rx_write_2(prq, BWN_PIO_RXCTL, BWN_PIO_RXCTL_DATAREADY);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_dma_newbuf(struct bwn_dma_ring *dr, struct bwn_dmadesc_generic *desc,
|
|
|
|
struct bwn_dmadesc_meta *meta, int init)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = dr->dr_mac;
|
|
|
|
struct bwn_dma *dma = &mac->mac_method.dma;
|
|
|
|
struct bwn_rxhdr4 *hdr;
|
|
|
|
bus_dmamap_t map;
|
|
|
|
bus_addr_t paddr;
|
|
|
|
struct mbuf *m;
|
|
|
|
int error;
|
|
|
|
|
2012-12-04 09:32:43 +00:00
|
|
|
m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (m == NULL) {
|
|
|
|
error = ENOBUFS;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the NIC is up and running, we need to:
|
|
|
|
* - Clear RX buffer's header.
|
|
|
|
* - Restore RX descriptor settings.
|
|
|
|
*/
|
|
|
|
if (init)
|
|
|
|
return (error);
|
|
|
|
else
|
|
|
|
goto back;
|
|
|
|
}
|
|
|
|
m->m_len = m->m_pkthdr.len = MCLBYTES;
|
|
|
|
|
|
|
|
bwn_dma_set_redzone(dr, m);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to load RX buf into temporary DMA map
|
|
|
|
*/
|
|
|
|
error = bus_dmamap_load_mbuf(dma->rxbuf_dtag, dr->dr_spare_dmap, m,
|
|
|
|
bwn_dma_buf_addr, &paddr, BUS_DMA_NOWAIT);
|
|
|
|
if (error) {
|
|
|
|
m_freem(m);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See the comment above
|
|
|
|
*/
|
|
|
|
if (init)
|
|
|
|
return (error);
|
|
|
|
else
|
|
|
|
goto back;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!init)
|
|
|
|
bus_dmamap_unload(dma->rxbuf_dtag, meta->mt_dmap);
|
|
|
|
meta->mt_m = m;
|
|
|
|
meta->mt_paddr = paddr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Swap RX buf's DMA map with the loaded temporary one
|
|
|
|
*/
|
|
|
|
map = meta->mt_dmap;
|
|
|
|
meta->mt_dmap = dr->dr_spare_dmap;
|
|
|
|
dr->dr_spare_dmap = map;
|
|
|
|
|
|
|
|
back:
|
|
|
|
/*
|
|
|
|
* Clear RX buf header
|
|
|
|
*/
|
|
|
|
hdr = mtod(meta->mt_m, struct bwn_rxhdr4 *);
|
|
|
|
bzero(hdr, sizeof(*hdr));
|
|
|
|
bus_dmamap_sync(dma->rxbuf_dtag, meta->mt_dmap,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup RX buf descriptor
|
|
|
|
*/
|
2013-05-06 21:57:44 +00:00
|
|
|
dr->setdesc(dr, desc, meta->mt_paddr, meta->mt_m->m_len -
|
2010-02-16 01:44:14 +00:00
|
|
|
sizeof(*hdr), 0, 0, 0);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_dma_buf_addr(void *arg, bus_dma_segment_t *seg, int nseg,
|
|
|
|
bus_size_t mapsz __unused, int error)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!error) {
|
|
|
|
KASSERT(nseg == 1, ("too many segments(%d)\n", nseg));
|
|
|
|
*((bus_addr_t *)arg) = seg->ds_addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_hwrate2ieeerate(int rate)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch (rate) {
|
|
|
|
case BWN_CCK_RATE_1MB:
|
|
|
|
return (2);
|
|
|
|
case BWN_CCK_RATE_2MB:
|
|
|
|
return (4);
|
|
|
|
case BWN_CCK_RATE_5MB:
|
|
|
|
return (11);
|
|
|
|
case BWN_CCK_RATE_11MB:
|
|
|
|
return (22);
|
|
|
|
case BWN_OFDM_RATE_6MB:
|
|
|
|
return (12);
|
|
|
|
case BWN_OFDM_RATE_9MB:
|
|
|
|
return (18);
|
|
|
|
case BWN_OFDM_RATE_12MB:
|
|
|
|
return (24);
|
|
|
|
case BWN_OFDM_RATE_18MB:
|
|
|
|
return (36);
|
|
|
|
case BWN_OFDM_RATE_24MB:
|
|
|
|
return (48);
|
|
|
|
case BWN_OFDM_RATE_36MB:
|
|
|
|
return (72);
|
|
|
|
case BWN_OFDM_RATE_48MB:
|
|
|
|
return (96);
|
|
|
|
case BWN_OFDM_RATE_54MB:
|
|
|
|
return (108);
|
|
|
|
default:
|
|
|
|
printf("Ooops\n");
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-05 07:04:38 +00:00
|
|
|
/*
|
|
|
|
* Post process the RX provided RSSI.
|
|
|
|
*
|
|
|
|
* Valid for A, B, G, LP PHYs.
|
|
|
|
*/
|
|
|
|
static int8_t
|
2016-05-05 19:40:26 +00:00
|
|
|
bwn_rx_rssi_calc(struct bwn_mac *mac, uint8_t in_rssi,
|
2016-05-05 07:04:38 +00:00
|
|
|
int ofdm, int adjust_2053, int adjust_2050)
|
|
|
|
{
|
|
|
|
struct bwn_phy *phy = &mac->mac_phy;
|
|
|
|
struct bwn_phy_g *gphy = &phy->phy_g;
|
|
|
|
int tmp;
|
|
|
|
|
|
|
|
switch (phy->rf_ver) {
|
|
|
|
case 0x2050:
|
|
|
|
if (ofdm) {
|
|
|
|
tmp = in_rssi;
|
|
|
|
if (tmp > 127)
|
|
|
|
tmp -= 256;
|
|
|
|
tmp = tmp * 73 / 64;
|
|
|
|
if (adjust_2050)
|
|
|
|
tmp += 25;
|
|
|
|
else
|
|
|
|
tmp -= 3;
|
|
|
|
} else {
|
2018-02-05 23:38:15 +00:00
|
|
|
if (mac->mac_sc->sc_board_info.board_flags
|
|
|
|
& BHND_BFL_ADCDIV) {
|
2016-05-05 07:04:38 +00:00
|
|
|
if (in_rssi > 63)
|
|
|
|
in_rssi = 63;
|
|
|
|
tmp = gphy->pg_nrssi_lt[in_rssi];
|
|
|
|
tmp = (31 - tmp) * -131 / 128 - 57;
|
|
|
|
} else {
|
|
|
|
tmp = in_rssi;
|
|
|
|
tmp = (31 - tmp) * -149 / 128 - 68;
|
|
|
|
}
|
|
|
|
if (phy->type == BWN_PHYTYPE_G && adjust_2050)
|
|
|
|
tmp += 25;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x2060:
|
|
|
|
if (in_rssi > 127)
|
|
|
|
tmp = in_rssi - 256;
|
|
|
|
else
|
|
|
|
tmp = in_rssi;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
tmp = in_rssi;
|
|
|
|
tmp = (tmp - 11) * 103 / 64;
|
|
|
|
if (adjust_2053)
|
|
|
|
tmp -= 109;
|
|
|
|
else
|
|
|
|
tmp -= 83;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (tmp);
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
static void
|
|
|
|
bwn_rxeof(struct bwn_mac *mac, struct mbuf *m, const void *_rxhdr)
|
|
|
|
{
|
|
|
|
const struct bwn_rxhdr4 *rxhdr = _rxhdr;
|
|
|
|
struct bwn_plcp6 *plcp;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
struct ieee80211_frame_min *wh;
|
|
|
|
struct ieee80211_node *ni;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2010-02-16 01:44:14 +00:00
|
|
|
uint32_t macstat;
|
2010-02-23 05:02:10 +00:00
|
|
|
int padding, rate, rssi = 0, noise = 0, type;
|
2010-02-16 01:44:14 +00:00
|
|
|
uint16_t phytype, phystat0, phystat3, chanstat;
|
|
|
|
unsigned char *mp = mtod(m, unsigned char *);
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
|
|
|
|
phystat0 = le16toh(rxhdr->phy_status0);
|
2016-05-05 07:04:38 +00:00
|
|
|
|
2016-05-18 05:56:25 +00:00
|
|
|
/*
|
|
|
|
* XXX Note: phy_status3 doesn't exist for HT-PHY; it's only
|
|
|
|
* used for LP-PHY.
|
|
|
|
*/
|
|
|
|
phystat3 = le16toh(rxhdr->ps3.lp.phy_status3);
|
|
|
|
|
|
|
|
switch (mac->mac_fw.fw_hdr_format) {
|
|
|
|
case BWN_FW_HDR_351:
|
|
|
|
case BWN_FW_HDR_410:
|
|
|
|
macstat = le32toh(rxhdr->ps4.r351.mac_status);
|
|
|
|
chanstat = le16toh(rxhdr->ps4.r351.channel);
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_598:
|
|
|
|
macstat = le32toh(rxhdr->ps4.r598.mac_status);
|
|
|
|
chanstat = le16toh(rxhdr->ps4.r598.channel);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-05-05 07:04:38 +00:00
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
phytype = chanstat & BWN_RX_CHAN_PHYTYPE;
|
|
|
|
|
|
|
|
if (macstat & BWN_RX_MAC_FCSERR)
|
|
|
|
device_printf(sc->sc_dev, "TODO RX: RX_FLAG_FAILED_FCS_CRC\n");
|
|
|
|
if (phystat0 & (BWN_RX_PHYST0_PLCPHCF | BWN_RX_PHYST0_PLCPFV))
|
|
|
|
device_printf(sc->sc_dev, "TODO RX: RX_FLAG_FAILED_PLCP_CRC\n");
|
|
|
|
if (macstat & BWN_RX_MAC_DECERR)
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
padding = (macstat & BWN_RX_MAC_PADDING) ? 2 : 0;
|
|
|
|
if (m->m_pkthdr.len < (sizeof(struct bwn_plcp6) + padding)) {
|
2010-02-19 03:36:02 +00:00
|
|
|
device_printf(sc->sc_dev, "frame too short (length=%d)\n",
|
|
|
|
m->m_pkthdr.len);
|
2010-02-16 01:44:14 +00:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
plcp = (struct bwn_plcp6 *)(mp + padding);
|
|
|
|
m_adj(m, sizeof(struct bwn_plcp6) + padding);
|
|
|
|
if (m->m_pkthdr.len < IEEE80211_MIN_LEN) {
|
2010-02-19 03:36:02 +00:00
|
|
|
device_printf(sc->sc_dev, "frame too short (length=%d)\n",
|
|
|
|
m->m_pkthdr.len);
|
2010-02-16 01:44:14 +00:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
wh = mtod(m, struct ieee80211_frame_min *);
|
|
|
|
|
2018-02-13 20:07:40 +00:00
|
|
|
if (macstat & BWN_RX_MAC_DEC) {
|
|
|
|
DPRINTF(sc, BWN_DEBUG_HWCRYPTO,
|
2010-02-19 03:36:02 +00:00
|
|
|
"RX decryption attempted (old %d keyidx %#x)\n",
|
|
|
|
BWN_ISOLDFMT(mac),
|
|
|
|
(macstat & BWN_RX_MAC_KEYIDX) >> BWN_RX_MAC_KEYIDX_SHIFT);
|
2018-02-13 20:07:40 +00:00
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
if (phystat0 & BWN_RX_PHYST0_OFDM)
|
|
|
|
rate = bwn_plcp_get_ofdmrate(mac, plcp,
|
|
|
|
phytype == BWN_PHYTYPE_A);
|
|
|
|
else
|
|
|
|
rate = bwn_plcp_get_cckrate(mac, plcp);
|
|
|
|
if (rate == -1) {
|
|
|
|
if (!(mac->mac_sc->sc_filters & BWN_MACCTL_PASS_BADPLCP))
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
sc->sc_rx_rate = bwn_hwrate2ieeerate(rate);
|
|
|
|
|
2016-05-05 07:04:38 +00:00
|
|
|
/* rssi/noise */
|
|
|
|
switch (phytype) {
|
|
|
|
case BWN_PHYTYPE_A:
|
|
|
|
case BWN_PHYTYPE_B:
|
|
|
|
case BWN_PHYTYPE_G:
|
|
|
|
case BWN_PHYTYPE_LP:
|
|
|
|
rssi = bwn_rx_rssi_calc(mac, rxhdr->phy.abg.rssi,
|
|
|
|
!! (phystat0 & BWN_RX_PHYST0_OFDM),
|
|
|
|
!! (phystat0 & BWN_RX_PHYST0_GAINCTL),
|
|
|
|
!! (phystat3 & BWN_RX_PHYST3_TRSTATE));
|
|
|
|
break;
|
2016-05-14 23:43:43 +00:00
|
|
|
case BWN_PHYTYPE_N:
|
|
|
|
/* Broadcom has code for min/avg, but always used max */
|
|
|
|
if (rxhdr->phy.n.power0 == 16 || rxhdr->phy.n.power0 == 32)
|
|
|
|
rssi = max(rxhdr->phy.n.power1, rxhdr->ps2.n.power2);
|
|
|
|
else
|
|
|
|
rssi = max(rxhdr->phy.n.power0, rxhdr->phy.n.power1);
|
2016-05-17 21:24:10 +00:00
|
|
|
#if 0
|
|
|
|
DPRINTF(mac->mac_sc, BWN_DEBUG_RECV,
|
|
|
|
"%s: power0=%d, power1=%d, power2=%d\n",
|
|
|
|
__func__,
|
|
|
|
rxhdr->phy.n.power0,
|
|
|
|
rxhdr->phy.n.power1,
|
|
|
|
rxhdr->ps2.n.power2);
|
|
|
|
#endif
|
2016-05-14 23:43:43 +00:00
|
|
|
break;
|
2016-05-05 07:04:38 +00:00
|
|
|
default:
|
|
|
|
/* XXX TODO: implement rssi for other PHYs */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-05-17 21:24:10 +00:00
|
|
|
/*
|
|
|
|
* RSSI here is absolute, not relative to the noise floor.
|
|
|
|
*/
|
2016-05-05 07:04:38 +00:00
|
|
|
noise = mac->mac_stats.link_noise;
|
2016-05-17 21:24:10 +00:00
|
|
|
rssi = rssi - noise;
|
2016-05-05 07:04:38 +00:00
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
/* RX radio tap */
|
|
|
|
if (ieee80211_radiotap_active(ic))
|
|
|
|
bwn_rx_radiotap(mac, m, rxhdr, plcp, rate, rssi, noise);
|
|
|
|
m_adj(m, -IEEE80211_CRC_LEN);
|
|
|
|
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
|
|
|
|
ni = ieee80211_find_rxnode(ic, wh);
|
|
|
|
if (ni != NULL) {
|
|
|
|
type = ieee80211_input(ni, m, rssi, noise);
|
|
|
|
ieee80211_free_node(ni);
|
|
|
|
} else
|
|
|
|
type = ieee80211_input_all(ic, m, rssi, noise);
|
|
|
|
|
|
|
|
BWN_LOCK(sc);
|
|
|
|
return;
|
|
|
|
drop:
|
|
|
|
device_printf(sc->sc_dev, "%s: dropped\n", __func__);
|
|
|
|
}
|
|
|
|
|
2016-10-02 20:35:55 +00:00
|
|
|
static void
|
|
|
|
bwn_ratectl_tx_complete(const struct ieee80211_node *ni,
|
|
|
|
const struct bwn_txstatus *status)
|
|
|
|
{
|
|
|
|
struct ieee80211_ratectl_tx_status txs;
|
|
|
|
int retrycnt = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we don't get an ACK, then we should log the
|
|
|
|
* full framecnt. That may be 0 if it's a PHY
|
|
|
|
* failure, so ensure that gets logged as some
|
|
|
|
* retry attempt.
|
|
|
|
*/
|
|
|
|
txs.flags = IEEE80211_RATECTL_STATUS_LONG_RETRY;
|
|
|
|
if (status->ack) {
|
|
|
|
txs.status = IEEE80211_RATECTL_TX_SUCCESS;
|
|
|
|
retrycnt = status->framecnt - 1;
|
|
|
|
} else {
|
|
|
|
txs.status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED;
|
|
|
|
retrycnt = status->framecnt;
|
|
|
|
if (retrycnt == 0)
|
|
|
|
retrycnt = 1;
|
|
|
|
}
|
|
|
|
txs.long_retries = retrycnt;
|
|
|
|
ieee80211_ratectl_tx_complete(ni, &txs);
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
static void
|
|
|
|
bwn_dma_handle_txeof(struct bwn_mac *mac,
|
|
|
|
const struct bwn_txstatus *status)
|
|
|
|
{
|
|
|
|
struct bwn_dma *dma = &mac->mac_method.dma;
|
|
|
|
struct bwn_dma_ring *dr;
|
|
|
|
struct bwn_dmadesc_generic *desc;
|
|
|
|
struct bwn_dmadesc_meta *meta;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
int slot;
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
|
|
|
|
dr = bwn_dma_parse_cookie(mac, status, status->cookie, &slot);
|
|
|
|
if (dr == NULL) {
|
|
|
|
device_printf(sc->sc_dev, "failed to parse cookie\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
KASSERT(dr->dr_tx, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
KASSERT(slot >= 0 && slot < dr->dr_numslots,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
dr->getdesc(dr, slot, &desc, &meta);
|
|
|
|
|
|
|
|
if (meta->mt_txtype == BWN_DMADESC_METATYPE_HEADER)
|
|
|
|
bus_dmamap_unload(dr->dr_txring_dtag, meta->mt_dmap);
|
|
|
|
else if (meta->mt_txtype == BWN_DMADESC_METATYPE_BODY)
|
|
|
|
bus_dmamap_unload(dma->txbuf_dtag, meta->mt_dmap);
|
|
|
|
|
|
|
|
if (meta->mt_islast) {
|
|
|
|
KASSERT(meta->mt_m != NULL,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
2016-10-02 20:35:55 +00:00
|
|
|
bwn_ratectl_tx_complete(meta->mt_ni, status);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
ieee80211_tx_complete(meta->mt_ni, meta->mt_m, 0);
|
|
|
|
meta->mt_ni = NULL;
|
2010-02-16 01:44:14 +00:00
|
|
|
meta->mt_m = NULL;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
} else
|
2010-02-16 01:44:14 +00:00
|
|
|
KASSERT(meta->mt_m == NULL,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
|
|
|
dr->dr_usedslot--;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
if (meta->mt_islast)
|
2010-02-16 01:44:14 +00:00
|
|
|
break;
|
|
|
|
slot = bwn_dma_nextslot(dr, slot);
|
|
|
|
}
|
|
|
|
sc->sc_watchdog_timer = 0;
|
|
|
|
if (dr->dr_stop) {
|
|
|
|
KASSERT(bwn_dma_freeslot(dr) >= BWN_TX_SLOTS_PER_FRAME,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
dr->dr_stop = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_pio_handle_txeof(struct bwn_mac *mac,
|
|
|
|
const struct bwn_txstatus *status)
|
|
|
|
{
|
|
|
|
struct bwn_pio_txqueue *tq;
|
|
|
|
struct bwn_pio_txpkt *tp = NULL;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
|
|
|
|
tq = bwn_pio_parse_cookie(mac, status->cookie, &tp);
|
|
|
|
if (tq == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
tq->tq_used -= roundup(tp->tp_m->m_pkthdr.len + BWN_HDRSIZE(mac), 4);
|
|
|
|
tq->tq_free++;
|
|
|
|
|
2016-10-02 20:35:55 +00:00
|
|
|
/* XXX ieee80211_tx_complete()? */
|
2010-02-16 01:44:14 +00:00
|
|
|
if (tp->tp_ni != NULL) {
|
|
|
|
/*
|
|
|
|
* Do any tx complete callback. Note this must
|
|
|
|
* be done before releasing the node reference.
|
|
|
|
*/
|
[bwn] accurately(ish) account transmit/recieve failures for rate control.
I noticed that it'd associate fine, but it'd quickly stop exchanging traffic.
Receive was okay, but transmit just failed.
Then I went "wlandebug +rate". I discovered it started at 36M OFDM, and then
quickly rose to 54M, which then showed 0% transmit success.
Then, I dug into how the completion path works. We are reading 'ack=0'
in the TX status side, so .. then I discovered we were only processing the
TX completion status /if/ ack=1. So, we'd only ever count successes;
we'd never count failures, and thus the rate control code thought
everything was a-ok.
We also have to set retrycnt to something non-zero so it indeed does
bring the rate down upon failure.
So:
* Delete the rate control completion code from the tx completion
routine, it's just duplicate and never worked. Putting it behind
'if (status->ack) was pointless.
* Move it to the PIO and DMA completion routines which actually
do free the node reference and mbuf. We know at that point
what the status is, so do it there.
* Fake a retrycnt of 1 for now, so we at least count failures.
Also:
* Start adding comments about weird stuff I find with rate selection.
In this instance, we shouldn't be selecting a fallback rate that
doesn't match the currently configured mode (11a, 11b, 11g, etc.)
This isn't perfect - AMRR does try 54mbit and takes a few packets
before it figures out it's a bad idea - but it's better than nothing.
This makes the bwn(4) driver actually useful for the first time since
I've tried using it - and that dates back to 2011. I've resisted
successfully until now.
Tested:
* Broadcom BCM4312 802.11b/g Wireless, STA mode
WLAN (chipid 0x4312 rev 15) PHY (analog 6 type 5 rev 1) RADIO (manuf 0x17f ver 0x2062 rev 2)
TODO:
* See if the fallback rate actually /is/ working
* Question my own sanity over touching this driver in the first place.
2016-05-04 01:36:19 +00:00
|
|
|
|
2016-10-02 20:35:55 +00:00
|
|
|
bwn_ratectl_tx_complete(tp->tp_ni, status);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (tp->tp_m->m_flags & M_TXCB)
|
|
|
|
ieee80211_process_callback(tp->tp_ni, tp->tp_m, 0);
|
|
|
|
ieee80211_free_node(tp->tp_ni);
|
|
|
|
tp->tp_ni = NULL;
|
|
|
|
}
|
|
|
|
m_freem(tp->tp_m);
|
|
|
|
tp->tp_m = NULL;
|
|
|
|
TAILQ_INSERT_TAIL(&tq->tq_pktlist, tp, tp_list);
|
|
|
|
|
|
|
|
sc->sc_watchdog_timer = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_phy_txpower_check(struct bwn_mac *mac, uint32_t flags)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
struct bwn_phy *phy = &mac->mac_phy;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2010-02-16 01:44:14 +00:00
|
|
|
unsigned long now;
|
2016-05-14 23:43:05 +00:00
|
|
|
bwn_txpwr_result_t result;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
BWN_GETTIME(now);
|
|
|
|
|
2016-03-30 06:48:09 +00:00
|
|
|
if (!(flags & BWN_TXPWR_IGNORE_TIME) && ieee80211_time_before(now, phy->nexttime))
|
2010-02-16 01:44:14 +00:00
|
|
|
return;
|
|
|
|
phy->nexttime = now + 2 * 1000;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
if (sc->sc_board_info.board_vendor == PCI_VENDOR_BROADCOM &&
|
|
|
|
sc->sc_board_info.board_type == BHND_BOARD_BU4306)
|
2010-02-16 01:44:14 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (phy->recalc_txpwr != NULL) {
|
|
|
|
result = phy->recalc_txpwr(mac,
|
|
|
|
(flags & BWN_TXPWR_IGNORE_TSSI) ? 1 : 0);
|
|
|
|
if (result == BWN_TXPWR_RES_DONE)
|
|
|
|
return;
|
|
|
|
KASSERT(result == BWN_TXPWR_RES_NEED_ADJUST,
|
|
|
|
("%s: fail", __func__));
|
|
|
|
KASSERT(phy->set_txpwr != NULL, ("%s: fail", __func__));
|
|
|
|
|
|
|
|
ieee80211_runtask(ic, &mac->mac_txpower);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
bwn_pio_rx_read_2(struct bwn_pio_rxqueue *prq, uint16_t offset)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (BWN_READ_2(prq->prq_mac, prq->prq_base + offset));
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
bwn_pio_rx_read_4(struct bwn_pio_rxqueue *prq, uint16_t offset)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (BWN_READ_4(prq->prq_mac, prq->prq_base + offset));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_pio_rx_write_2(struct bwn_pio_rxqueue *prq, uint16_t offset, uint16_t value)
|
|
|
|
{
|
|
|
|
|
|
|
|
BWN_WRITE_2(prq->prq_mac, prq->prq_base + offset, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_pio_rx_write_4(struct bwn_pio_rxqueue *prq, uint16_t offset, uint32_t value)
|
|
|
|
{
|
|
|
|
|
|
|
|
BWN_WRITE_4(prq->prq_mac, prq->prq_base + offset, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_ieeerate2hwrate(struct bwn_softc *sc, int rate)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch (rate) {
|
|
|
|
/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
|
|
|
|
case 12:
|
|
|
|
return (BWN_OFDM_RATE_6MB);
|
|
|
|
case 18:
|
|
|
|
return (BWN_OFDM_RATE_9MB);
|
|
|
|
case 24:
|
|
|
|
return (BWN_OFDM_RATE_12MB);
|
|
|
|
case 36:
|
|
|
|
return (BWN_OFDM_RATE_18MB);
|
|
|
|
case 48:
|
|
|
|
return (BWN_OFDM_RATE_24MB);
|
|
|
|
case 72:
|
|
|
|
return (BWN_OFDM_RATE_36MB);
|
|
|
|
case 96:
|
|
|
|
return (BWN_OFDM_RATE_48MB);
|
|
|
|
case 108:
|
|
|
|
return (BWN_OFDM_RATE_54MB);
|
|
|
|
/* CCK rates (NB: not IEEE std, device-specific) */
|
|
|
|
case 2:
|
|
|
|
return (BWN_CCK_RATE_1MB);
|
|
|
|
case 4:
|
|
|
|
return (BWN_CCK_RATE_2MB);
|
|
|
|
case 11:
|
|
|
|
return (BWN_CCK_RATE_5MB);
|
|
|
|
case 22:
|
|
|
|
return (BWN_CCK_RATE_11MB);
|
|
|
|
}
|
|
|
|
|
|
|
|
device_printf(sc->sc_dev, "unsupported rate %d\n", rate);
|
|
|
|
return (BWN_CCK_RATE_1MB);
|
|
|
|
}
|
|
|
|
|
2016-05-14 23:48:26 +00:00
|
|
|
static uint16_t
|
|
|
|
bwn_set_txhdr_phyctl1(struct bwn_mac *mac, uint8_t bitrate)
|
|
|
|
{
|
|
|
|
struct bwn_phy *phy = &mac->mac_phy;
|
|
|
|
uint16_t control = 0;
|
|
|
|
uint16_t bw;
|
|
|
|
|
|
|
|
/* XXX TODO: this is for LP phy, what about N-PHY, etc? */
|
|
|
|
bw = BWN_TXH_PHY1_BW_20;
|
|
|
|
|
|
|
|
if (BWN_ISCCKRATE(bitrate) && phy->type != BWN_PHYTYPE_LP) {
|
|
|
|
control = bw;
|
|
|
|
} else {
|
|
|
|
control = bw;
|
|
|
|
/* Figure out coding rate and modulation */
|
|
|
|
/* XXX TODO: table-ize, for MCS transmit */
|
|
|
|
/* Note: this is BWN_*_RATE values */
|
|
|
|
switch (bitrate) {
|
|
|
|
case BWN_CCK_RATE_1MB:
|
|
|
|
control |= 0;
|
|
|
|
break;
|
|
|
|
case BWN_CCK_RATE_2MB:
|
|
|
|
control |= 1;
|
|
|
|
break;
|
|
|
|
case BWN_CCK_RATE_5MB:
|
|
|
|
control |= 2;
|
|
|
|
break;
|
|
|
|
case BWN_CCK_RATE_11MB:
|
|
|
|
control |= 3;
|
|
|
|
break;
|
|
|
|
case BWN_OFDM_RATE_6MB:
|
|
|
|
control |= BWN_TXH_PHY1_CRATE_1_2;
|
|
|
|
control |= BWN_TXH_PHY1_MODUL_BPSK;
|
|
|
|
break;
|
|
|
|
case BWN_OFDM_RATE_9MB:
|
|
|
|
control |= BWN_TXH_PHY1_CRATE_3_4;
|
|
|
|
control |= BWN_TXH_PHY1_MODUL_BPSK;
|
|
|
|
break;
|
|
|
|
case BWN_OFDM_RATE_12MB:
|
|
|
|
control |= BWN_TXH_PHY1_CRATE_1_2;
|
|
|
|
control |= BWN_TXH_PHY1_MODUL_QPSK;
|
|
|
|
break;
|
|
|
|
case BWN_OFDM_RATE_18MB:
|
|
|
|
control |= BWN_TXH_PHY1_CRATE_3_4;
|
|
|
|
control |= BWN_TXH_PHY1_MODUL_QPSK;
|
|
|
|
break;
|
|
|
|
case BWN_OFDM_RATE_24MB:
|
|
|
|
control |= BWN_TXH_PHY1_CRATE_1_2;
|
|
|
|
control |= BWN_TXH_PHY1_MODUL_QAM16;
|
|
|
|
break;
|
|
|
|
case BWN_OFDM_RATE_36MB:
|
|
|
|
control |= BWN_TXH_PHY1_CRATE_3_4;
|
|
|
|
control |= BWN_TXH_PHY1_MODUL_QAM16;
|
|
|
|
break;
|
|
|
|
case BWN_OFDM_RATE_48MB:
|
|
|
|
control |= BWN_TXH_PHY1_CRATE_1_2;
|
|
|
|
control |= BWN_TXH_PHY1_MODUL_QAM64;
|
|
|
|
break;
|
|
|
|
case BWN_OFDM_RATE_54MB:
|
|
|
|
control |= BWN_TXH_PHY1_CRATE_3_4;
|
|
|
|
control |= BWN_TXH_PHY1_MODUL_QAM64;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
control |= BWN_TXH_PHY1_MODE_SISO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return control;
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
static int
|
|
|
|
bwn_set_txhdr(struct bwn_mac *mac, struct ieee80211_node *ni,
|
|
|
|
struct mbuf *m, struct bwn_txhdr *txhdr, uint16_t cookie)
|
|
|
|
{
|
|
|
|
const struct bwn_phy *phy = &mac->mac_phy;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
struct ieee80211_frame *wh;
|
|
|
|
struct ieee80211_frame *protwh;
|
2017-02-26 20:49:35 +00:00
|
|
|
const struct ieee80211_txparam *tp = ni->ni_txparms;
|
2010-02-16 01:44:14 +00:00
|
|
|
struct ieee80211vap *vap = ni->ni_vap;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2010-02-16 01:44:14 +00:00
|
|
|
struct mbuf *mprot;
|
2018-03-09 11:33:56 +00:00
|
|
|
uint8_t *prot_ptr;
|
2010-02-16 01:44:14 +00:00
|
|
|
unsigned int len;
|
|
|
|
uint32_t macctl = 0;
|
2018-03-09 11:33:56 +00:00
|
|
|
int rts_rate, rts_rate_fb, ismcast, isshort, rix, type;
|
2010-02-16 01:44:14 +00:00
|
|
|
uint16_t phyctl = 0;
|
|
|
|
uint8_t rate, rate_fb;
|
2016-05-14 23:48:26 +00:00
|
|
|
int fill_phy_ctl1 = 0;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
|
|
memset(txhdr, 0, sizeof(*txhdr));
|
|
|
|
|
|
|
|
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
|
|
|
|
ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
|
|
|
|
isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
|
|
|
|
|
2016-05-14 23:48:26 +00:00
|
|
|
if ((phy->type == BWN_PHYTYPE_N) || (phy->type == BWN_PHYTYPE_LP)
|
|
|
|
|| (phy->type == BWN_PHYTYPE_HT))
|
|
|
|
fill_phy_ctl1 = 1;
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
/*
|
|
|
|
* Find TX rate
|
|
|
|
*/
|
|
|
|
if (type != IEEE80211_FC0_TYPE_DATA || (m->m_flags & M_EAPOL))
|
|
|
|
rate = rate_fb = tp->mgmtrate;
|
|
|
|
else if (ismcast)
|
|
|
|
rate = rate_fb = tp->mcastrate;
|
|
|
|
else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
|
|
|
|
rate = rate_fb = tp->ucastrate;
|
|
|
|
else {
|
2010-04-07 15:29:13 +00:00
|
|
|
rix = ieee80211_ratectl_rate(ni, NULL, 0);
|
2010-02-16 01:44:14 +00:00
|
|
|
rate = ni->ni_txrate;
|
|
|
|
|
|
|
|
if (rix > 0)
|
|
|
|
rate_fb = ni->ni_rates.rs_rates[rix - 1] &
|
|
|
|
IEEE80211_RATE_VAL;
|
|
|
|
else
|
|
|
|
rate_fb = rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_tx_rate = rate;
|
|
|
|
|
[bwn] accurately(ish) account transmit/recieve failures for rate control.
I noticed that it'd associate fine, but it'd quickly stop exchanging traffic.
Receive was okay, but transmit just failed.
Then I went "wlandebug +rate". I discovered it started at 36M OFDM, and then
quickly rose to 54M, which then showed 0% transmit success.
Then, I dug into how the completion path works. We are reading 'ack=0'
in the TX status side, so .. then I discovered we were only processing the
TX completion status /if/ ack=1. So, we'd only ever count successes;
we'd never count failures, and thus the rate control code thought
everything was a-ok.
We also have to set retrycnt to something non-zero so it indeed does
bring the rate down upon failure.
So:
* Delete the rate control completion code from the tx completion
routine, it's just duplicate and never worked. Putting it behind
'if (status->ack) was pointless.
* Move it to the PIO and DMA completion routines which actually
do free the node reference and mbuf. We know at that point
what the status is, so do it there.
* Fake a retrycnt of 1 for now, so we at least count failures.
Also:
* Start adding comments about weird stuff I find with rate selection.
In this instance, we shouldn't be selecting a fallback rate that
doesn't match the currently configured mode (11a, 11b, 11g, etc.)
This isn't perfect - AMRR does try 54mbit and takes a few packets
before it figures out it's a bad idea - but it's better than nothing.
This makes the bwn(4) driver actually useful for the first time since
I've tried using it - and that dates back to 2011. I've resisted
successfully until now.
Tested:
* Broadcom BCM4312 802.11b/g Wireless, STA mode
WLAN (chipid 0x4312 rev 15) PHY (analog 6 type 5 rev 1) RADIO (manuf 0x17f ver 0x2062 rev 2)
TODO:
* See if the fallback rate actually /is/ working
* Question my own sanity over touching this driver in the first place.
2016-05-04 01:36:19 +00:00
|
|
|
/* Note: this maps the select ieee80211 rate to hardware rate */
|
2010-02-16 01:44:14 +00:00
|
|
|
rate = bwn_ieeerate2hwrate(sc, rate);
|
|
|
|
rate_fb = bwn_ieeerate2hwrate(sc, rate_fb);
|
|
|
|
|
|
|
|
txhdr->phyrate = (BWN_ISOFDMRATE(rate)) ? bwn_plcp_getofdm(rate) :
|
|
|
|
bwn_plcp_getcck(rate);
|
|
|
|
bcopy(wh->i_fc, txhdr->macfc, sizeof(txhdr->macfc));
|
|
|
|
bcopy(wh->i_addr1, txhdr->addr1, IEEE80211_ADDR_LEN);
|
|
|
|
|
[bwn] accurately(ish) account transmit/recieve failures for rate control.
I noticed that it'd associate fine, but it'd quickly stop exchanging traffic.
Receive was okay, but transmit just failed.
Then I went "wlandebug +rate". I discovered it started at 36M OFDM, and then
quickly rose to 54M, which then showed 0% transmit success.
Then, I dug into how the completion path works. We are reading 'ack=0'
in the TX status side, so .. then I discovered we were only processing the
TX completion status /if/ ack=1. So, we'd only ever count successes;
we'd never count failures, and thus the rate control code thought
everything was a-ok.
We also have to set retrycnt to something non-zero so it indeed does
bring the rate down upon failure.
So:
* Delete the rate control completion code from the tx completion
routine, it's just duplicate and never worked. Putting it behind
'if (status->ack) was pointless.
* Move it to the PIO and DMA completion routines which actually
do free the node reference and mbuf. We know at that point
what the status is, so do it there.
* Fake a retrycnt of 1 for now, so we at least count failures.
Also:
* Start adding comments about weird stuff I find with rate selection.
In this instance, we shouldn't be selecting a fallback rate that
doesn't match the currently configured mode (11a, 11b, 11g, etc.)
This isn't perfect - AMRR does try 54mbit and takes a few packets
before it figures out it's a bad idea - but it's better than nothing.
This makes the bwn(4) driver actually useful for the first time since
I've tried using it - and that dates back to 2011. I've resisted
successfully until now.
Tested:
* Broadcom BCM4312 802.11b/g Wireless, STA mode
WLAN (chipid 0x4312 rev 15) PHY (analog 6 type 5 rev 1) RADIO (manuf 0x17f ver 0x2062 rev 2)
TODO:
* See if the fallback rate actually /is/ working
* Question my own sanity over touching this driver in the first place.
2016-05-04 01:36:19 +00:00
|
|
|
/* XXX rate/rate_fb is the hardware rate */
|
2010-02-16 01:44:14 +00:00
|
|
|
if ((rate_fb == rate) ||
|
|
|
|
(*(u_int16_t *)wh->i_dur & htole16(0x8000)) ||
|
|
|
|
(*(u_int16_t *)wh->i_dur == htole16(0)))
|
|
|
|
txhdr->dur_fb = *(u_int16_t *)wh->i_dur;
|
|
|
|
else
|
|
|
|
txhdr->dur_fb = ieee80211_compute_duration(ic->ic_rt,
|
|
|
|
m->m_pkthdr.len, rate, isshort);
|
|
|
|
|
|
|
|
/* XXX TX encryption */
|
2016-05-18 05:56:25 +00:00
|
|
|
|
|
|
|
switch (mac->mac_fw.fw_hdr_format) {
|
|
|
|
case BWN_FW_HDR_351:
|
|
|
|
bwn_plcp_genhdr((struct bwn_plcp4 *)(&txhdr->body.r351.plcp),
|
|
|
|
m->m_pkthdr.len + IEEE80211_CRC_LEN, rate);
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_410:
|
|
|
|
bwn_plcp_genhdr((struct bwn_plcp4 *)(&txhdr->body.r410.plcp),
|
|
|
|
m->m_pkthdr.len + IEEE80211_CRC_LEN, rate);
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_598:
|
|
|
|
bwn_plcp_genhdr((struct bwn_plcp4 *)(&txhdr->body.r598.plcp),
|
|
|
|
m->m_pkthdr.len + IEEE80211_CRC_LEN, rate);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_plcp_genhdr((struct bwn_plcp4 *)(&txhdr->plcp_fb),
|
|
|
|
m->m_pkthdr.len + IEEE80211_CRC_LEN, rate_fb);
|
|
|
|
|
|
|
|
txhdr->eftypes |= (BWN_ISOFDMRATE(rate_fb)) ? BWN_TX_EFT_FB_OFDM :
|
|
|
|
BWN_TX_EFT_FB_CCK;
|
|
|
|
txhdr->chan = phy->chan;
|
|
|
|
phyctl |= (BWN_ISOFDMRATE(rate)) ? BWN_TX_PHY_ENC_OFDM :
|
|
|
|
BWN_TX_PHY_ENC_CCK;
|
[bwn] accurately(ish) account transmit/recieve failures for rate control.
I noticed that it'd associate fine, but it'd quickly stop exchanging traffic.
Receive was okay, but transmit just failed.
Then I went "wlandebug +rate". I discovered it started at 36M OFDM, and then
quickly rose to 54M, which then showed 0% transmit success.
Then, I dug into how the completion path works. We are reading 'ack=0'
in the TX status side, so .. then I discovered we were only processing the
TX completion status /if/ ack=1. So, we'd only ever count successes;
we'd never count failures, and thus the rate control code thought
everything was a-ok.
We also have to set retrycnt to something non-zero so it indeed does
bring the rate down upon failure.
So:
* Delete the rate control completion code from the tx completion
routine, it's just duplicate and never worked. Putting it behind
'if (status->ack) was pointless.
* Move it to the PIO and DMA completion routines which actually
do free the node reference and mbuf. We know at that point
what the status is, so do it there.
* Fake a retrycnt of 1 for now, so we at least count failures.
Also:
* Start adding comments about weird stuff I find with rate selection.
In this instance, we shouldn't be selecting a fallback rate that
doesn't match the currently configured mode (11a, 11b, 11g, etc.)
This isn't perfect - AMRR does try 54mbit and takes a few packets
before it figures out it's a bad idea - but it's better than nothing.
This makes the bwn(4) driver actually useful for the first time since
I've tried using it - and that dates back to 2011. I've resisted
successfully until now.
Tested:
* Broadcom BCM4312 802.11b/g Wireless, STA mode
WLAN (chipid 0x4312 rev 15) PHY (analog 6 type 5 rev 1) RADIO (manuf 0x17f ver 0x2062 rev 2)
TODO:
* See if the fallback rate actually /is/ working
* Question my own sanity over touching this driver in the first place.
2016-05-04 01:36:19 +00:00
|
|
|
/* XXX preamble? obey net80211 */
|
2010-02-16 01:44:14 +00:00
|
|
|
if (isshort && (rate == BWN_CCK_RATE_2MB || rate == BWN_CCK_RATE_5MB ||
|
|
|
|
rate == BWN_CCK_RATE_11MB))
|
|
|
|
phyctl |= BWN_TX_PHY_SHORTPRMBL;
|
|
|
|
|
2016-05-14 23:45:47 +00:00
|
|
|
if (! phy->gmode)
|
|
|
|
macctl |= BWN_TX_MAC_5GHZ;
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
/* XXX TX antenna selection */
|
|
|
|
|
|
|
|
switch (bwn_antenna_sanitize(mac, 0)) {
|
|
|
|
case 0:
|
|
|
|
phyctl |= BWN_TX_PHY_ANT01AUTO;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
phyctl |= BWN_TX_PHY_ANT0;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
phyctl |= BWN_TX_PHY_ANT1;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
phyctl |= BWN_TX_PHY_ANT2;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
phyctl |= BWN_TX_PHY_ANT3;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ismcast)
|
|
|
|
macctl |= BWN_TX_MAC_ACK;
|
|
|
|
|
|
|
|
macctl |= (BWN_TX_MAC_HWSEQ | BWN_TX_MAC_START_MSDU);
|
|
|
|
if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
|
|
|
|
m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
|
|
|
|
macctl |= BWN_TX_MAC_LONGFRAME;
|
|
|
|
|
2018-03-09 11:33:56 +00:00
|
|
|
if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
|
|
|
|
ic->ic_protmode != IEEE80211_PROT_NONE) {
|
2016-06-06 07:09:02 +00:00
|
|
|
/* Note: don't fall back to CCK rates for 5G */
|
|
|
|
if (phy->gmode)
|
|
|
|
rts_rate = BWN_CCK_RATE_1MB;
|
|
|
|
else
|
|
|
|
rts_rate = BWN_OFDM_RATE_6MB;
|
2010-02-16 01:44:14 +00:00
|
|
|
rts_rate_fb = bwn_get_fbrate(rts_rate);
|
|
|
|
|
[bwn] accurately(ish) account transmit/recieve failures for rate control.
I noticed that it'd associate fine, but it'd quickly stop exchanging traffic.
Receive was okay, but transmit just failed.
Then I went "wlandebug +rate". I discovered it started at 36M OFDM, and then
quickly rose to 54M, which then showed 0% transmit success.
Then, I dug into how the completion path works. We are reading 'ack=0'
in the TX status side, so .. then I discovered we were only processing the
TX completion status /if/ ack=1. So, we'd only ever count successes;
we'd never count failures, and thus the rate control code thought
everything was a-ok.
We also have to set retrycnt to something non-zero so it indeed does
bring the rate down upon failure.
So:
* Delete the rate control completion code from the tx completion
routine, it's just duplicate and never worked. Putting it behind
'if (status->ack) was pointless.
* Move it to the PIO and DMA completion routines which actually
do free the node reference and mbuf. We know at that point
what the status is, so do it there.
* Fake a retrycnt of 1 for now, so we at least count failures.
Also:
* Start adding comments about weird stuff I find with rate selection.
In this instance, we shouldn't be selecting a fallback rate that
doesn't match the currently configured mode (11a, 11b, 11g, etc.)
This isn't perfect - AMRR does try 54mbit and takes a few packets
before it figures out it's a bad idea - but it's better than nothing.
This makes the bwn(4) driver actually useful for the first time since
I've tried using it - and that dates back to 2011. I've resisted
successfully until now.
Tested:
* Broadcom BCM4312 802.11b/g Wireless, STA mode
WLAN (chipid 0x4312 rev 15) PHY (analog 6 type 5 rev 1) RADIO (manuf 0x17f ver 0x2062 rev 2)
TODO:
* See if the fallback rate actually /is/ working
* Question my own sanity over touching this driver in the first place.
2016-05-04 01:36:19 +00:00
|
|
|
/* XXX 'rate' here is hardware rate now, not the net80211 rate */
|
2018-03-09 11:33:56 +00:00
|
|
|
mprot = ieee80211_alloc_prot(ni, m, rate, ic->ic_protmode);
|
|
|
|
if (mprot == NULL) {
|
|
|
|
if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, 1);
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"could not allocate mbuf for protection mode %d\n",
|
|
|
|
ic->ic_protmode);
|
|
|
|
return (ENOBUFS);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-03-09 11:33:56 +00:00
|
|
|
switch (mac->mac_fw.fw_hdr_format) {
|
|
|
|
case BWN_FW_HDR_351:
|
|
|
|
prot_ptr = txhdr->body.r351.rts_frame;
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_410:
|
|
|
|
prot_ptr = txhdr->body.r410.rts_frame;
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_598:
|
|
|
|
prot_ptr = txhdr->body.r598.rts_frame;
|
|
|
|
break;
|
|
|
|
}
|
2016-05-18 05:56:25 +00:00
|
|
|
|
2018-03-09 11:33:56 +00:00
|
|
|
bcopy(mtod(mprot, uint8_t *), prot_ptr, mprot->m_pkthdr.len);
|
|
|
|
m_freem(mprot);
|
2016-05-18 05:56:25 +00:00
|
|
|
|
2018-03-09 11:33:56 +00:00
|
|
|
if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) {
|
2010-02-16 01:44:14 +00:00
|
|
|
macctl |= BWN_TX_MAC_SEND_CTSTOSELF;
|
|
|
|
len = sizeof(struct ieee80211_frame_cts);
|
|
|
|
} else {
|
|
|
|
macctl |= BWN_TX_MAC_SEND_RTSCTS;
|
|
|
|
len = sizeof(struct ieee80211_frame_rts);
|
|
|
|
}
|
|
|
|
len += IEEE80211_CRC_LEN;
|
2016-05-18 05:56:25 +00:00
|
|
|
|
|
|
|
switch (mac->mac_fw.fw_hdr_format) {
|
|
|
|
case BWN_FW_HDR_351:
|
|
|
|
bwn_plcp_genhdr((struct bwn_plcp4 *)
|
|
|
|
&txhdr->body.r351.rts_plcp, len, rts_rate);
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_410:
|
|
|
|
bwn_plcp_genhdr((struct bwn_plcp4 *)
|
|
|
|
&txhdr->body.r410.rts_plcp, len, rts_rate);
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_598:
|
|
|
|
bwn_plcp_genhdr((struct bwn_plcp4 *)
|
|
|
|
&txhdr->body.r598.rts_plcp, len, rts_rate);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_plcp_genhdr((struct bwn_plcp4 *)&txhdr->rts_plcp_fb, len,
|
|
|
|
rts_rate_fb);
|
|
|
|
|
2016-05-18 05:56:25 +00:00
|
|
|
switch (mac->mac_fw.fw_hdr_format) {
|
|
|
|
case BWN_FW_HDR_351:
|
|
|
|
protwh = (struct ieee80211_frame *)
|
|
|
|
&txhdr->body.r351.rts_frame;
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_410:
|
|
|
|
protwh = (struct ieee80211_frame *)
|
|
|
|
&txhdr->body.r410.rts_frame;
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_598:
|
|
|
|
protwh = (struct ieee80211_frame *)
|
|
|
|
&txhdr->body.r598.rts_frame;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
txhdr->rts_dur_fb = *(u_int16_t *)protwh->i_dur;
|
|
|
|
|
|
|
|
if (BWN_ISOFDMRATE(rts_rate)) {
|
|
|
|
txhdr->eftypes |= BWN_TX_EFT_RTS_OFDM;
|
|
|
|
txhdr->phyrate_rts = bwn_plcp_getofdm(rts_rate);
|
|
|
|
} else {
|
|
|
|
txhdr->eftypes |= BWN_TX_EFT_RTS_CCK;
|
|
|
|
txhdr->phyrate_rts = bwn_plcp_getcck(rts_rate);
|
|
|
|
}
|
|
|
|
txhdr->eftypes |= (BWN_ISOFDMRATE(rts_rate_fb)) ?
|
|
|
|
BWN_TX_EFT_RTS_FBOFDM : BWN_TX_EFT_RTS_FBCCK;
|
2016-05-14 23:48:26 +00:00
|
|
|
|
|
|
|
if (fill_phy_ctl1) {
|
|
|
|
txhdr->phyctl_1rts = htole16(bwn_set_txhdr_phyctl1(mac, rts_rate));
|
|
|
|
txhdr->phyctl_1rtsfb = htole16(bwn_set_txhdr_phyctl1(mac, rts_rate_fb));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fill_phy_ctl1) {
|
|
|
|
txhdr->phyctl_1 = htole16(bwn_set_txhdr_phyctl1(mac, rate));
|
|
|
|
txhdr->phyctl_1fb = htole16(bwn_set_txhdr_phyctl1(mac, rate_fb));
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-18 05:56:25 +00:00
|
|
|
switch (mac->mac_fw.fw_hdr_format) {
|
|
|
|
case BWN_FW_HDR_351:
|
|
|
|
txhdr->body.r351.cookie = htole16(cookie);
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_410:
|
|
|
|
txhdr->body.r410.cookie = htole16(cookie);
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_598:
|
|
|
|
txhdr->body.r598.cookie = htole16(cookie);
|
|
|
|
break;
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
txhdr->macctl = htole32(macctl);
|
|
|
|
txhdr->phyctl = htole16(phyctl);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TX radio tap
|
|
|
|
*/
|
|
|
|
if (ieee80211_radiotap_active_vap(vap)) {
|
|
|
|
sc->sc_tx_th.wt_flags = 0;
|
2014-01-08 08:06:56 +00:00
|
|
|
if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED)
|
2010-02-16 01:44:14 +00:00
|
|
|
sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
|
|
|
|
if (isshort &&
|
|
|
|
(rate == BWN_CCK_RATE_2MB || rate == BWN_CCK_RATE_5MB ||
|
|
|
|
rate == BWN_CCK_RATE_11MB))
|
|
|
|
sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
|
|
|
|
sc->sc_tx_th.wt_rate = rate;
|
|
|
|
|
|
|
|
ieee80211_radiotap_tx(vap, m);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_plcp_genhdr(struct bwn_plcp4 *plcp, const uint16_t octets,
|
|
|
|
const uint8_t rate)
|
|
|
|
{
|
|
|
|
uint32_t d, plen;
|
|
|
|
uint8_t *raw = plcp->o.raw;
|
|
|
|
|
|
|
|
if (BWN_ISOFDMRATE(rate)) {
|
|
|
|
d = bwn_plcp_getofdm(rate);
|
|
|
|
KASSERT(!(octets & 0xf000),
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
d |= (octets << 5);
|
|
|
|
plcp->o.data = htole32(d);
|
|
|
|
} else {
|
|
|
|
plen = octets * 16 / rate;
|
|
|
|
if ((octets * 16 % rate) > 0) {
|
|
|
|
plen++;
|
|
|
|
if ((rate == BWN_CCK_RATE_11MB)
|
|
|
|
&& ((octets * 8 % 11) < 4)) {
|
|
|
|
raw[1] = 0x84;
|
|
|
|
} else
|
|
|
|
raw[1] = 0x04;
|
|
|
|
} else
|
|
|
|
raw[1] = 0x04;
|
|
|
|
plcp->o.data |= htole32(plen << 16);
|
|
|
|
raw[0] = bwn_plcp_getcck(rate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
bwn_antenna_sanitize(struct bwn_mac *mac, uint8_t n)
|
|
|
|
{
|
2010-03-09 19:58:00 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2010-02-16 01:44:14 +00:00
|
|
|
uint8_t mask;
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
return (0);
|
|
|
|
if (mac->mac_phy.gmode)
|
2018-02-05 23:38:15 +00:00
|
|
|
mask = sc->sc_ant2g;
|
2010-02-16 01:44:14 +00:00
|
|
|
else
|
2018-02-05 23:38:15 +00:00
|
|
|
mask = sc->sc_ant5g;
|
2010-02-16 01:44:14 +00:00
|
|
|
if (!(mask & (1 << (n - 1))))
|
|
|
|
return (0);
|
|
|
|
return (n);
|
|
|
|
}
|
|
|
|
|
2016-05-04 01:29:00 +00:00
|
|
|
/*
|
|
|
|
* Return a fallback rate for the given rate.
|
|
|
|
*
|
|
|
|
* Note: Don't fall back from OFDM to CCK.
|
|
|
|
*/
|
2010-02-16 01:44:14 +00:00
|
|
|
static uint8_t
|
|
|
|
bwn_get_fbrate(uint8_t bitrate)
|
|
|
|
{
|
|
|
|
switch (bitrate) {
|
2016-05-04 01:29:00 +00:00
|
|
|
/* CCK */
|
2010-02-16 01:44:14 +00:00
|
|
|
case BWN_CCK_RATE_1MB:
|
|
|
|
return (BWN_CCK_RATE_1MB);
|
|
|
|
case BWN_CCK_RATE_2MB:
|
|
|
|
return (BWN_CCK_RATE_1MB);
|
|
|
|
case BWN_CCK_RATE_5MB:
|
|
|
|
return (BWN_CCK_RATE_2MB);
|
|
|
|
case BWN_CCK_RATE_11MB:
|
|
|
|
return (BWN_CCK_RATE_5MB);
|
2016-05-04 01:29:00 +00:00
|
|
|
|
|
|
|
/* OFDM */
|
2010-02-16 01:44:14 +00:00
|
|
|
case BWN_OFDM_RATE_6MB:
|
2016-05-04 01:29:00 +00:00
|
|
|
return (BWN_OFDM_RATE_6MB);
|
2010-02-16 01:44:14 +00:00
|
|
|
case BWN_OFDM_RATE_9MB:
|
|
|
|
return (BWN_OFDM_RATE_6MB);
|
|
|
|
case BWN_OFDM_RATE_12MB:
|
|
|
|
return (BWN_OFDM_RATE_9MB);
|
|
|
|
case BWN_OFDM_RATE_18MB:
|
|
|
|
return (BWN_OFDM_RATE_12MB);
|
|
|
|
case BWN_OFDM_RATE_24MB:
|
|
|
|
return (BWN_OFDM_RATE_18MB);
|
|
|
|
case BWN_OFDM_RATE_36MB:
|
|
|
|
return (BWN_OFDM_RATE_24MB);
|
|
|
|
case BWN_OFDM_RATE_48MB:
|
|
|
|
return (BWN_OFDM_RATE_36MB);
|
|
|
|
case BWN_OFDM_RATE_54MB:
|
|
|
|
return (BWN_OFDM_RATE_48MB);
|
|
|
|
}
|
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
bwn_pio_write_multi_4(struct bwn_mac *mac, struct bwn_pio_txqueue *tq,
|
|
|
|
uint32_t ctl, const void *_data, int len)
|
|
|
|
{
|
2010-03-09 19:58:00 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2010-02-16 01:44:14 +00:00
|
|
|
uint32_t value = 0;
|
|
|
|
const uint8_t *data = _data;
|
|
|
|
|
|
|
|
ctl |= BWN_PIO8_TXCTL_0_7 | BWN_PIO8_TXCTL_8_15 |
|
|
|
|
BWN_PIO8_TXCTL_16_23 | BWN_PIO8_TXCTL_24_31;
|
|
|
|
bwn_pio_write_4(mac, tq, BWN_PIO8_TXCTL, ctl);
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
bus_write_multi_4(sc->sc_mem_res, tq->tq_base + BWN_PIO8_TXDATA,
|
|
|
|
__DECONST(void *, data), (len & ~3));
|
2010-02-16 01:44:14 +00:00
|
|
|
if (len & 3) {
|
|
|
|
ctl &= ~(BWN_PIO8_TXCTL_8_15 | BWN_PIO8_TXCTL_16_23 |
|
|
|
|
BWN_PIO8_TXCTL_24_31);
|
|
|
|
data = &(data[len - 1]);
|
|
|
|
switch (len & 3) {
|
|
|
|
case 3:
|
|
|
|
ctl |= BWN_PIO8_TXCTL_16_23;
|
|
|
|
value |= (uint32_t)(*data) << 16;
|
|
|
|
data--;
|
|
|
|
case 2:
|
|
|
|
ctl |= BWN_PIO8_TXCTL_8_15;
|
|
|
|
value |= (uint32_t)(*data) << 8;
|
|
|
|
data--;
|
|
|
|
case 1:
|
|
|
|
value |= (uint32_t)(*data);
|
|
|
|
}
|
|
|
|
bwn_pio_write_4(mac, tq, BWN_PIO8_TXCTL, ctl);
|
|
|
|
bwn_pio_write_4(mac, tq, BWN_PIO8_TXDATA, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ctl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_pio_write_4(struct bwn_mac *mac, struct bwn_pio_txqueue *tq,
|
|
|
|
uint16_t offset, uint32_t value)
|
|
|
|
{
|
|
|
|
|
|
|
|
BWN_WRITE_4(mac, tq->tq_base + offset, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
bwn_pio_write_multi_2(struct bwn_mac *mac, struct bwn_pio_txqueue *tq,
|
|
|
|
uint16_t ctl, const void *_data, int len)
|
|
|
|
{
|
2010-03-09 19:58:00 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2010-02-16 01:44:14 +00:00
|
|
|
const uint8_t *data = _data;
|
|
|
|
|
|
|
|
ctl |= BWN_PIO_TXCTL_WRITELO | BWN_PIO_TXCTL_WRITEHI;
|
|
|
|
BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXCTL, ctl);
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
bus_write_multi_2(sc->sc_mem_res, tq->tq_base + BWN_PIO_TXDATA,
|
|
|
|
__DECONST(void *, data), (len & ~1));
|
2010-02-16 01:44:14 +00:00
|
|
|
if (len & 1) {
|
|
|
|
ctl &= ~BWN_PIO_TXCTL_WRITEHI;
|
|
|
|
BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXCTL, ctl);
|
|
|
|
BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXDATA, data[len - 1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ctl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
bwn_pio_write_mbuf_2(struct bwn_mac *mac, struct bwn_pio_txqueue *tq,
|
|
|
|
uint16_t ctl, struct mbuf *m0)
|
|
|
|
{
|
|
|
|
int i, j = 0;
|
|
|
|
uint16_t data = 0;
|
|
|
|
const uint8_t *buf;
|
|
|
|
struct mbuf *m = m0;
|
|
|
|
|
|
|
|
ctl |= BWN_PIO_TXCTL_WRITELO | BWN_PIO_TXCTL_WRITEHI;
|
|
|
|
BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXCTL, ctl);
|
|
|
|
|
|
|
|
for (; m != NULL; m = m->m_next) {
|
|
|
|
buf = mtod(m, const uint8_t *);
|
|
|
|
for (i = 0; i < m->m_len; i++) {
|
|
|
|
if (!((j++) % 2))
|
|
|
|
data |= buf[i];
|
|
|
|
else {
|
|
|
|
data |= (buf[i] << 8);
|
|
|
|
BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXDATA, data);
|
|
|
|
data = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m0->m_pkthdr.len % 2) {
|
|
|
|
ctl &= ~BWN_PIO_TXCTL_WRITEHI;
|
|
|
|
BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXCTL, ctl);
|
|
|
|
BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXDATA, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ctl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_set_slot_time(struct bwn_mac *mac, uint16_t time)
|
|
|
|
{
|
|
|
|
|
2016-05-14 23:45:16 +00:00
|
|
|
/* XXX should exit if 5GHz band .. */
|
2010-02-16 01:44:14 +00:00
|
|
|
if (mac->mac_phy.type != BWN_PHYTYPE_G)
|
|
|
|
return;
|
2016-05-14 23:45:16 +00:00
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
BWN_WRITE_2(mac, 0x684, 510 + time);
|
2016-05-14 23:45:16 +00:00
|
|
|
/* Disabled in Linux b43, can adversely effect performance */
|
|
|
|
#if 0
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_shm_write_2(mac, BWN_SHARED, 0x0010, time);
|
2016-05-14 23:45:16 +00:00
|
|
|
#endif
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct bwn_dma_ring *
|
|
|
|
bwn_dma_select(struct bwn_mac *mac, uint8_t prio)
|
|
|
|
{
|
|
|
|
|
|
|
|
if ((mac->mac_flags & BWN_MAC_FLAG_WME) == 0)
|
|
|
|
return (mac->mac_method.dma.wme[WME_AC_BE]);
|
|
|
|
|
|
|
|
switch (prio) {
|
|
|
|
case 3:
|
|
|
|
return (mac->mac_method.dma.wme[WME_AC_VO]);
|
|
|
|
case 2:
|
|
|
|
return (mac->mac_method.dma.wme[WME_AC_VI]);
|
|
|
|
case 0:
|
|
|
|
return (mac->mac_method.dma.wme[WME_AC_BE]);
|
|
|
|
case 1:
|
|
|
|
return (mac->mac_method.dma.wme[WME_AC_BK]);
|
|
|
|
}
|
|
|
|
KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__));
|
2010-02-23 05:02:10 +00:00
|
|
|
return (NULL);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_dma_getslot(struct bwn_dma_ring *dr)
|
|
|
|
{
|
|
|
|
int slot;
|
|
|
|
|
2010-02-23 05:02:10 +00:00
|
|
|
BWN_ASSERT_LOCKED(dr->dr_mac->mac_sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
KASSERT(dr->dr_tx, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
KASSERT(!(dr->dr_stop), ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
KASSERT(bwn_dma_freeslot(dr) != 0, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
|
|
|
slot = bwn_dma_nextslot(dr, dr->dr_curslot);
|
|
|
|
KASSERT(!(slot & ~0x0fff), ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
dr->dr_curslot = slot;
|
|
|
|
dr->dr_usedslot++;
|
|
|
|
|
|
|
|
return (slot);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bwn_pio_txqueue *
|
|
|
|
bwn_pio_parse_cookie(struct bwn_mac *mac, uint16_t cookie,
|
|
|
|
struct bwn_pio_txpkt **pack)
|
|
|
|
{
|
|
|
|
struct bwn_pio *pio = &mac->mac_method.pio;
|
|
|
|
struct bwn_pio_txqueue *tq = NULL;
|
|
|
|
unsigned int index;
|
|
|
|
|
|
|
|
switch (cookie & 0xf000) {
|
|
|
|
case 0x1000:
|
|
|
|
tq = &pio->wme[WME_AC_BK];
|
|
|
|
break;
|
|
|
|
case 0x2000:
|
|
|
|
tq = &pio->wme[WME_AC_BE];
|
|
|
|
break;
|
|
|
|
case 0x3000:
|
|
|
|
tq = &pio->wme[WME_AC_VI];
|
|
|
|
break;
|
|
|
|
case 0x4000:
|
|
|
|
tq = &pio->wme[WME_AC_VO];
|
|
|
|
break;
|
|
|
|
case 0x5000:
|
|
|
|
tq = &pio->mcast;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
KASSERT(tq != NULL, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
if (tq == NULL)
|
|
|
|
return (NULL);
|
|
|
|
index = (cookie & 0x0fff);
|
|
|
|
KASSERT(index < N(tq->tq_pkts), ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
if (index >= N(tq->tq_pkts))
|
|
|
|
return (NULL);
|
|
|
|
*pack = &tq->tq_pkts[index];
|
|
|
|
KASSERT(*pack != NULL, ("%s:%d: fail", __func__, __LINE__));
|
|
|
|
return (tq);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_txpwr(void *arg, int npending)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = arg;
|
2017-04-06 17:08:59 +00:00
|
|
|
struct bwn_softc *sc;
|
|
|
|
|
|
|
|
if (mac == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sc = mac->mac_sc;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
BWN_LOCK(sc);
|
2017-04-06 17:08:59 +00:00
|
|
|
if (mac->mac_status >= BWN_MAC_STATUS_STARTED &&
|
2010-02-16 01:44:14 +00:00
|
|
|
mac->mac_phy.set_txpwr != NULL)
|
|
|
|
mac->mac_phy.set_txpwr(mac);
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_task_15s(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
uint16_t reg;
|
|
|
|
|
|
|
|
if (mac->mac_fw.opensource) {
|
|
|
|
reg = bwn_shm_read_2(mac, BWN_SCRATCH, BWN_WATCHDOG_REG);
|
|
|
|
if (reg) {
|
|
|
|
bwn_restart(mac, "fw watchdog");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bwn_shm_write_2(mac, BWN_SCRATCH, BWN_WATCHDOG_REG, 1);
|
|
|
|
}
|
|
|
|
if (mac->mac_phy.task_15s)
|
|
|
|
mac->mac_phy.task_15s(mac);
|
|
|
|
|
|
|
|
mac->mac_phy.txerrors = BWN_TXERROR_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_task_30s(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (mac->mac_phy.type != BWN_PHYTYPE_G || mac->mac_noise.noi_running)
|
|
|
|
return;
|
|
|
|
mac->mac_noise.noi_running = 1;
|
|
|
|
mac->mac_noise.noi_nsamples = 0;
|
|
|
|
|
|
|
|
bwn_noise_gensample(mac);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_task_60s(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (mac->mac_phy.task_60s)
|
|
|
|
mac->mac_phy.task_60s(mac);
|
|
|
|
bwn_phy_txpower_check(mac, BWN_TXPWR_IGNORE_TIME);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_tasks(void *arg)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = arg;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(sc);
|
|
|
|
if (mac->mac_status != BWN_MAC_STATUS_STARTED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (mac->mac_task_state % 4 == 0)
|
|
|
|
bwn_task_60s(mac);
|
|
|
|
if (mac->mac_task_state % 2 == 0)
|
|
|
|
bwn_task_30s(mac);
|
|
|
|
bwn_task_15s(mac);
|
|
|
|
|
|
|
|
mac->mac_task_state++;
|
|
|
|
callout_reset(&sc->sc_task_ch, hz * 15, bwn_tasks, mac);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_plcp_get_ofdmrate(struct bwn_mac *mac, struct bwn_plcp6 *plcp, uint8_t a)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
|
|
|
|
KASSERT(a == 0, ("not support APHY\n"));
|
|
|
|
|
|
|
|
switch (plcp->o.raw[0] & 0xf) {
|
|
|
|
case 0xb:
|
|
|
|
return (BWN_OFDM_RATE_6MB);
|
|
|
|
case 0xf:
|
|
|
|
return (BWN_OFDM_RATE_9MB);
|
|
|
|
case 0xa:
|
|
|
|
return (BWN_OFDM_RATE_12MB);
|
|
|
|
case 0xe:
|
|
|
|
return (BWN_OFDM_RATE_18MB);
|
|
|
|
case 0x9:
|
|
|
|
return (BWN_OFDM_RATE_24MB);
|
|
|
|
case 0xd:
|
|
|
|
return (BWN_OFDM_RATE_36MB);
|
|
|
|
case 0x8:
|
|
|
|
return (BWN_OFDM_RATE_48MB);
|
|
|
|
case 0xc:
|
|
|
|
return (BWN_OFDM_RATE_54MB);
|
|
|
|
}
|
|
|
|
device_printf(sc->sc_dev, "incorrect OFDM rate %d\n",
|
|
|
|
plcp->o.raw[0] & 0xf);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_plcp_get_cckrate(struct bwn_mac *mac, struct bwn_plcp6 *plcp)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
|
|
|
|
switch (plcp->o.raw[0]) {
|
|
|
|
case 0x0a:
|
|
|
|
return (BWN_CCK_RATE_1MB);
|
|
|
|
case 0x14:
|
|
|
|
return (BWN_CCK_RATE_2MB);
|
|
|
|
case 0x37:
|
|
|
|
return (BWN_CCK_RATE_5MB);
|
|
|
|
case 0x6e:
|
|
|
|
return (BWN_CCK_RATE_11MB);
|
|
|
|
}
|
|
|
|
device_printf(sc->sc_dev, "incorrect CCK rate %d\n", plcp->o.raw[0]);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_rx_radiotap(struct bwn_mac *mac, struct mbuf *m,
|
|
|
|
const struct bwn_rxhdr4 *rxhdr, struct bwn_plcp6 *plcp, int rate,
|
|
|
|
int rssi, int noise)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
const struct ieee80211_frame_min *wh;
|
|
|
|
uint64_t tsf;
|
|
|
|
uint16_t low_mactime_now;
|
2016-05-18 05:56:25 +00:00
|
|
|
uint16_t mt;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
if (htole16(rxhdr->phy_status0) & BWN_RX_PHYST0_SHORTPRMBL)
|
|
|
|
sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
|
|
|
|
|
|
|
|
wh = mtod(m, const struct ieee80211_frame_min *);
|
2014-01-08 08:06:56 +00:00
|
|
|
if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED)
|
2010-02-16 01:44:14 +00:00
|
|
|
sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_WEP;
|
|
|
|
|
|
|
|
bwn_tsf_read(mac, &tsf);
|
|
|
|
low_mactime_now = tsf;
|
|
|
|
tsf = tsf & ~0xffffULL;
|
2016-05-18 05:56:25 +00:00
|
|
|
|
|
|
|
switch (mac->mac_fw.fw_hdr_format) {
|
|
|
|
case BWN_FW_HDR_351:
|
|
|
|
case BWN_FW_HDR_410:
|
|
|
|
mt = le16toh(rxhdr->ps4.r351.mac_time);
|
|
|
|
break;
|
|
|
|
case BWN_FW_HDR_598:
|
|
|
|
mt = le16toh(rxhdr->ps4.r598.mac_time);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tsf += mt;
|
|
|
|
if (low_mactime_now < mt)
|
2010-02-16 01:44:14 +00:00
|
|
|
tsf -= 0x10000;
|
|
|
|
|
|
|
|
sc->sc_rx_th.wr_tsf = tsf;
|
|
|
|
sc->sc_rx_th.wr_rate = rate;
|
|
|
|
sc->sc_rx_th.wr_antsignal = rssi;
|
|
|
|
sc->sc_rx_th.wr_antnoise = noise;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_tsf_read(struct bwn_mac *mac, uint64_t *tsf)
|
|
|
|
{
|
|
|
|
uint32_t low, high;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
KASSERT(bhnd_get_hwrev(mac->mac_sc->sc_dev) >= 3,
|
2010-02-16 01:44:14 +00:00
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
|
|
|
|
low = BWN_READ_4(mac, BWN_REV3PLUS_TSF_LOW);
|
|
|
|
high = BWN_READ_4(mac, BWN_REV3PLUS_TSF_HIGH);
|
|
|
|
*tsf = high;
|
|
|
|
*tsf <<= 32;
|
|
|
|
*tsf |= low;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_dma_attach(struct bwn_mac *mac)
|
|
|
|
{
|
2018-02-05 23:38:15 +00:00
|
|
|
struct bwn_dma *dma;
|
|
|
|
struct bwn_softc *sc;
|
|
|
|
struct bhnd_dma_translation *dt, dma_translation;
|
|
|
|
bhnd_addr_t addrext_req;
|
|
|
|
bus_dma_tag_t dmat;
|
|
|
|
bus_addr_t lowaddr;
|
|
|
|
u_int addrext_shift, addr_width;
|
|
|
|
int error;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
dma = &mac->mac_method.dma;
|
|
|
|
sc = mac->mac_sc;
|
|
|
|
dt = NULL;
|
|
|
|
|
|
|
|
if (sc->sc_quirks & BWN_QUIRK_NODMA)
|
2010-02-16 01:44:14 +00:00
|
|
|
return (0);
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
KASSERT(bhnd_get_hwrev(sc->sc_dev) >= 5, ("%s: fail", __func__));
|
|
|
|
|
|
|
|
/* Use the DMA engine's maximum host address width to determine the
|
|
|
|
* addrext constraints, and supported device address width. */
|
|
|
|
switch (mac->mac_dmatype) {
|
|
|
|
case BHND_DMA_ADDR_30BIT:
|
|
|
|
/* 32-bit engine without addrext support */
|
|
|
|
addrext_req = 0x0;
|
|
|
|
addrext_shift = 0;
|
|
|
|
|
|
|
|
/* We can address the full 32-bit device address space */
|
|
|
|
addr_width = BHND_DMA_ADDR_32BIT;
|
|
|
|
break;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
case BHND_DMA_ADDR_32BIT:
|
|
|
|
/* 32-bit engine with addrext support */
|
|
|
|
addrext_req = BWN_DMA32_ADDREXT_MASK;
|
|
|
|
addrext_shift = BWN_DMA32_ADDREXT_SHIFT;
|
|
|
|
addr_width = BHND_DMA_ADDR_32BIT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BHND_DMA_ADDR_64BIT:
|
|
|
|
/* 64-bit engine with addrext support */
|
|
|
|
addrext_req = BWN_DMA64_ADDREXT_MASK;
|
|
|
|
addrext_shift = BWN_DMA64_ADDREXT_SHIFT;
|
|
|
|
addr_width = BHND_DMA_ADDR_64BIT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
device_printf(sc->sc_dev, "unsupported DMA address width: %d\n",
|
|
|
|
mac->mac_dmatype);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Fetch our device->host DMA translation and tag */
|
|
|
|
error = bhnd_get_dma_translation(sc->sc_dev, addr_width, 0, &dmat,
|
|
|
|
&dma_translation);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "error fetching DMA translation: "
|
|
|
|
"%d\n", error);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Verify that our DMA engine's addrext constraints are compatible with
|
|
|
|
* our DMA translation */
|
|
|
|
if (addrext_req != 0x0 &&
|
|
|
|
(dma_translation.addrext_mask & addrext_req) != addrext_req)
|
|
|
|
{
|
|
|
|
device_printf(sc->sc_dev, "bus addrext mask %#jx incompatible "
|
|
|
|
"with device addrext mask %#jx, disabling extended address "
|
|
|
|
"support\n", (uintmax_t)dma_translation.addrext_mask,
|
|
|
|
(uintmax_t)addrext_req);
|
|
|
|
|
|
|
|
addrext_req = 0x0;
|
|
|
|
addrext_shift = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Apply our addrext translation constraint */
|
|
|
|
dma_translation.addrext_mask = addrext_req;
|
|
|
|
|
|
|
|
/* Initialize our DMA engine configuration */
|
2010-02-16 01:44:14 +00:00
|
|
|
mac->mac_flags |= BWN_MAC_FLAG_DMA;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
dma->addrext_shift = addrext_shift;
|
|
|
|
dma->translation = dma_translation;
|
|
|
|
|
|
|
|
dt = &dma->translation;
|
|
|
|
|
|
|
|
/* Dermine our translation's maximum supported address */
|
|
|
|
lowaddr = MIN((dt->addr_mask | dt->addrext_mask), BUS_SPACE_MAXADDR);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create top level DMA tag
|
|
|
|
*/
|
2018-02-05 23:38:15 +00:00
|
|
|
error = bus_dma_tag_create(dmat, /* parent */
|
2010-02-16 01:44:14 +00:00
|
|
|
BWN_ALIGN, 0, /* alignment, bounds */
|
|
|
|
lowaddr, /* lowaddr */
|
|
|
|
BUS_SPACE_MAXADDR, /* highaddr */
|
|
|
|
NULL, NULL, /* filter, filterarg */
|
2015-03-22 16:10:28 +00:00
|
|
|
BUS_SPACE_MAXSIZE, /* maxsize */
|
2010-02-16 01:44:14 +00:00
|
|
|
BUS_SPACE_UNRESTRICTED, /* nsegments */
|
|
|
|
BUS_SPACE_MAXSIZE, /* maxsegsize */
|
|
|
|
0, /* flags */
|
|
|
|
NULL, NULL, /* lockfunc, lockarg */
|
|
|
|
&dma->parent_dtag);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "can't create parent DMA tag\n");
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create TX/RX mbuf DMA tag
|
|
|
|
*/
|
|
|
|
error = bus_dma_tag_create(dma->parent_dtag,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
BUS_SPACE_MAXADDR,
|
|
|
|
BUS_SPACE_MAXADDR,
|
|
|
|
NULL, NULL,
|
|
|
|
MCLBYTES,
|
|
|
|
1,
|
|
|
|
BUS_SPACE_MAXSIZE_32BIT,
|
|
|
|
0,
|
|
|
|
NULL, NULL,
|
|
|
|
&dma->rxbuf_dtag);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "can't create mbuf DMA tag\n");
|
|
|
|
goto fail0;
|
|
|
|
}
|
|
|
|
error = bus_dma_tag_create(dma->parent_dtag,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
BUS_SPACE_MAXADDR,
|
|
|
|
BUS_SPACE_MAXADDR,
|
|
|
|
NULL, NULL,
|
|
|
|
MCLBYTES,
|
|
|
|
1,
|
|
|
|
BUS_SPACE_MAXSIZE_32BIT,
|
|
|
|
0,
|
|
|
|
NULL, NULL,
|
|
|
|
&dma->txbuf_dtag);
|
|
|
|
if (error) {
|
|
|
|
device_printf(sc->sc_dev, "can't create mbuf DMA tag\n");
|
|
|
|
goto fail1;
|
|
|
|
}
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
dma->wme[WME_AC_BK] = bwn_dma_ringsetup(mac, 0, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (!dma->wme[WME_AC_BK])
|
|
|
|
goto fail2;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
dma->wme[WME_AC_BE] = bwn_dma_ringsetup(mac, 1, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (!dma->wme[WME_AC_BE])
|
|
|
|
goto fail3;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
dma->wme[WME_AC_VI] = bwn_dma_ringsetup(mac, 2, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (!dma->wme[WME_AC_VI])
|
|
|
|
goto fail4;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
dma->wme[WME_AC_VO] = bwn_dma_ringsetup(mac, 3, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (!dma->wme[WME_AC_VO])
|
|
|
|
goto fail5;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
dma->mcast = bwn_dma_ringsetup(mac, 4, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (!dma->mcast)
|
|
|
|
goto fail6;
|
2018-02-05 23:38:15 +00:00
|
|
|
dma->rx = bwn_dma_ringsetup(mac, 0, 0);
|
2010-02-16 01:44:14 +00:00
|
|
|
if (!dma->rx)
|
|
|
|
goto fail7;
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
fail7: bwn_dma_ringfree(&dma->mcast);
|
|
|
|
fail6: bwn_dma_ringfree(&dma->wme[WME_AC_VO]);
|
|
|
|
fail5: bwn_dma_ringfree(&dma->wme[WME_AC_VI]);
|
|
|
|
fail4: bwn_dma_ringfree(&dma->wme[WME_AC_BE]);
|
|
|
|
fail3: bwn_dma_ringfree(&dma->wme[WME_AC_BK]);
|
|
|
|
fail2: bus_dma_tag_destroy(dma->txbuf_dtag);
|
|
|
|
fail1: bus_dma_tag_destroy(dma->rxbuf_dtag);
|
|
|
|
fail0: bus_dma_tag_destroy(dma->parent_dtag);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct bwn_dma_ring *
|
|
|
|
bwn_dma_parse_cookie(struct bwn_mac *mac, const struct bwn_txstatus *status,
|
|
|
|
uint16_t cookie, int *slot)
|
|
|
|
{
|
|
|
|
struct bwn_dma *dma = &mac->mac_method.dma;
|
|
|
|
struct bwn_dma_ring *dr;
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
|
|
|
|
BWN_ASSERT_LOCKED(mac->mac_sc);
|
|
|
|
|
|
|
|
switch (cookie & 0xf000) {
|
|
|
|
case 0x1000:
|
|
|
|
dr = dma->wme[WME_AC_BK];
|
|
|
|
break;
|
|
|
|
case 0x2000:
|
|
|
|
dr = dma->wme[WME_AC_BE];
|
|
|
|
break;
|
|
|
|
case 0x3000:
|
|
|
|
dr = dma->wme[WME_AC_VI];
|
|
|
|
break;
|
|
|
|
case 0x4000:
|
|
|
|
dr = dma->wme[WME_AC_VO];
|
|
|
|
break;
|
|
|
|
case 0x5000:
|
|
|
|
dr = dma->mcast;
|
|
|
|
break;
|
|
|
|
default:
|
2010-02-23 05:02:10 +00:00
|
|
|
dr = NULL;
|
2010-02-16 01:44:14 +00:00
|
|
|
KASSERT(0 == 1,
|
|
|
|
("invalid cookie value %d", cookie & 0xf000));
|
|
|
|
}
|
|
|
|
*slot = (cookie & 0x0fff);
|
|
|
|
if (*slot < 0 || *slot >= dr->dr_numslots) {
|
|
|
|
/*
|
|
|
|
* XXX FIXME: sometimes H/W returns TX DONE events duplicately
|
|
|
|
* that it occurs events which have same H/W sequence numbers.
|
|
|
|
* When it's occurred just prints a WARNING msgs and ignores.
|
|
|
|
*/
|
|
|
|
KASSERT(status->seq == dma->lastseq,
|
|
|
|
("%s:%d: fail", __func__, __LINE__));
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"out of slot ranges (0 < %d < %d)\n", *slot,
|
|
|
|
dr->dr_numslots);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
dma->lastseq = status->seq;
|
|
|
|
return (dr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_dma_stop(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_dma *dma;
|
|
|
|
|
|
|
|
if ((mac->mac_flags & BWN_MAC_FLAG_DMA) == 0)
|
|
|
|
return;
|
|
|
|
dma = &mac->mac_method.dma;
|
|
|
|
|
|
|
|
bwn_dma_ringstop(&dma->rx);
|
|
|
|
bwn_dma_ringstop(&dma->wme[WME_AC_BK]);
|
|
|
|
bwn_dma_ringstop(&dma->wme[WME_AC_BE]);
|
|
|
|
bwn_dma_ringstop(&dma->wme[WME_AC_VI]);
|
|
|
|
bwn_dma_ringstop(&dma->wme[WME_AC_VO]);
|
|
|
|
bwn_dma_ringstop(&dma->mcast);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_dma_ringstop(struct bwn_dma_ring **dr)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (dr == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bwn_dma_cleanup(*dr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_pio_stop(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_pio *pio;
|
|
|
|
|
|
|
|
if (mac->mac_flags & BWN_MAC_FLAG_DMA)
|
|
|
|
return;
|
|
|
|
pio = &mac->mac_method.pio;
|
|
|
|
|
|
|
|
bwn_destroy_queue_tx(&pio->mcast);
|
|
|
|
bwn_destroy_queue_tx(&pio->wme[WME_AC_VO]);
|
|
|
|
bwn_destroy_queue_tx(&pio->wme[WME_AC_VI]);
|
|
|
|
bwn_destroy_queue_tx(&pio->wme[WME_AC_BE]);
|
|
|
|
bwn_destroy_queue_tx(&pio->wme[WME_AC_BK]);
|
|
|
|
}
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
static int
|
2010-02-16 01:44:14 +00:00
|
|
|
bwn_led_attach(struct bwn_mac *mac)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
const uint8_t *led_act = NULL;
|
2018-02-05 23:38:15 +00:00
|
|
|
int error;
|
2010-02-16 01:44:14 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
sc->sc_led_idle = (2350 * hz) / 1000;
|
|
|
|
sc->sc_led_blink = 1;
|
|
|
|
|
|
|
|
for (i = 0; i < N(bwn_vendor_led_act); ++i) {
|
2018-02-05 23:38:15 +00:00
|
|
|
if (sc->sc_board_info.board_vendor ==
|
2010-03-09 19:58:00 +00:00
|
|
|
bwn_vendor_led_act[i].vid) {
|
2010-02-16 01:44:14 +00:00
|
|
|
led_act = bwn_vendor_led_act[i].led_act;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (led_act == NULL)
|
|
|
|
led_act = bwn_default_led_act;
|
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
_Static_assert(nitems(bwn_led_vars) == BWN_LED_MAX,
|
|
|
|
"invalid NVRAM variable name array");
|
2010-02-16 01:44:14 +00:00
|
|
|
|
|
|
|
for (i = 0; i < BWN_LED_MAX; ++i) {
|
2018-02-05 23:38:15 +00:00
|
|
|
struct bwn_led *led;
|
|
|
|
uint8_t val;
|
|
|
|
|
|
|
|
led = &sc->sc_leds[i];
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2018-02-05 23:38:15 +00:00
|
|
|
KASSERT(i < nitems(bwn_led_vars), ("unknown LED index"));
|
|
|
|
error = bhnd_nvram_getvar_uint8(sc->sc_dev, bwn_led_vars[i],
|
|
|
|
&val);
|
|
|
|
if (error) {
|
|
|
|
if (error != ENOENT) {
|
|
|
|
device_printf(sc->sc_dev, "NVRAM variable %s "
|
|
|
|
"unreadable: %d", bwn_led_vars[i], error);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not found; use default */
|
2010-02-16 01:44:14 +00:00
|
|
|
led->led_act = led_act[i];
|
|
|
|
} else {
|
2018-02-05 23:38:15 +00:00
|
|
|
if (val & BWN_LED_ACT_LOW)
|
2010-02-16 01:44:14 +00:00
|
|
|
led->led_flags |= BWN_LED_F_ACTLOW;
|
2018-02-05 23:38:15 +00:00
|
|
|
led->led_act = val & BWN_LED_ACT_MASK;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
led->led_mask = (1 << i);
|
|
|
|
|
|
|
|
if (led->led_act == BWN_LED_ACT_BLINK_SLOW ||
|
|
|
|
led->led_act == BWN_LED_ACT_BLINK_POLL ||
|
|
|
|
led->led_act == BWN_LED_ACT_BLINK) {
|
|
|
|
led->led_flags |= BWN_LED_F_BLINK;
|
|
|
|
if (led->led_act == BWN_LED_ACT_BLINK_POLL)
|
|
|
|
led->led_flags |= BWN_LED_F_POLLABLE;
|
|
|
|
else if (led->led_act == BWN_LED_ACT_BLINK_SLOW)
|
|
|
|
led->led_flags |= BWN_LED_F_SLOW;
|
|
|
|
|
|
|
|
if (sc->sc_blink_led == NULL) {
|
|
|
|
sc->sc_blink_led = led;
|
|
|
|
if (led->led_flags & BWN_LED_F_SLOW)
|
|
|
|
BWN_LED_SLOWDOWN(sc->sc_led_idle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTF(sc, BWN_DEBUG_LED,
|
|
|
|
"%dth led, act %d, lowact %d\n", i,
|
|
|
|
led->led_act, led->led_flags & BWN_LED_F_ACTLOW);
|
|
|
|
}
|
|
|
|
callout_init_mtx(&sc->sc_led_blink_ch, &sc->sc_mtx, 0);
|
2018-02-05 23:38:15 +00:00
|
|
|
|
|
|
|
return (0);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static __inline uint16_t
|
|
|
|
bwn_led_onoff(const struct bwn_led *led, uint16_t val, int on)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (led->led_flags & BWN_LED_F_ACTLOW)
|
|
|
|
on = !on;
|
|
|
|
if (on)
|
|
|
|
val |= led->led_mask;
|
|
|
|
else
|
|
|
|
val &= ~led->led_mask;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_led_newstate(struct bwn_mac *mac, enum ieee80211_state nstate)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
2010-02-16 01:44:14 +00:00
|
|
|
uint16_t val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (nstate == IEEE80211_S_INIT) {
|
|
|
|
callout_stop(&sc->sc_led_blink_ch);
|
|
|
|
sc->sc_led_blinking = 0;
|
|
|
|
}
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
if ((sc->sc_flags & BWN_FLAG_RUNNING) == 0)
|
2010-02-16 01:44:14 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
val = BWN_READ_2(mac, BWN_GPIO_CONTROL);
|
|
|
|
for (i = 0; i < BWN_LED_MAX; ++i) {
|
|
|
|
struct bwn_led *led = &sc->sc_leds[i];
|
|
|
|
int on;
|
|
|
|
|
|
|
|
if (led->led_act == BWN_LED_ACT_UNKN ||
|
|
|
|
led->led_act == BWN_LED_ACT_NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ((led->led_flags & BWN_LED_F_BLINK) &&
|
|
|
|
nstate != IEEE80211_S_INIT)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (led->led_act) {
|
|
|
|
case BWN_LED_ACT_ON: /* Always on */
|
|
|
|
on = 1;
|
|
|
|
break;
|
|
|
|
case BWN_LED_ACT_OFF: /* Always off */
|
|
|
|
case BWN_LED_ACT_5GHZ: /* TODO: 11A */
|
|
|
|
on = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
on = 1;
|
|
|
|
switch (nstate) {
|
|
|
|
case IEEE80211_S_INIT:
|
|
|
|
on = 0;
|
|
|
|
break;
|
|
|
|
case IEEE80211_S_RUN:
|
|
|
|
if (led->led_act == BWN_LED_ACT_11G &&
|
|
|
|
ic->ic_curmode != IEEE80211_MODE_11G)
|
|
|
|
on = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (led->led_act == BWN_LED_ACT_ASSOC)
|
|
|
|
on = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
val = bwn_led_onoff(led, val, on);
|
|
|
|
}
|
|
|
|
BWN_WRITE_2(mac, BWN_GPIO_CONTROL, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_led_event(struct bwn_mac *mac, int event)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2010-03-09 19:58:00 +00:00
|
|
|
struct bwn_led *led = sc->sc_blink_led;
|
|
|
|
int rate;
|
|
|
|
|
|
|
|
if (event == BWN_LED_EVENT_POLL) {
|
|
|
|
if ((led->led_flags & BWN_LED_F_POLLABLE) == 0)
|
|
|
|
return;
|
|
|
|
if (ticks - sc->sc_led_ticks < sc->sc_led_idle)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_led_ticks = ticks;
|
|
|
|
if (sc->sc_led_blinking)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case BWN_LED_EVENT_RX:
|
|
|
|
rate = sc->sc_rx_rate;
|
|
|
|
break;
|
|
|
|
case BWN_LED_EVENT_TX:
|
|
|
|
rate = sc->sc_tx_rate;
|
|
|
|
break;
|
|
|
|
case BWN_LED_EVENT_POLL:
|
|
|
|
rate = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
panic("unknown LED event %d\n", event);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bwn_led_blink_start(mac, bwn_led_duration[rate].on_dur,
|
|
|
|
bwn_led_duration[rate].off_dur);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_led_blink_start(struct bwn_mac *mac, int on_dur, int off_dur)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2010-03-09 19:58:00 +00:00
|
|
|
struct bwn_led *led = sc->sc_blink_led;
|
|
|
|
uint16_t val;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2010-03-09 19:58:00 +00:00
|
|
|
val = BWN_READ_2(mac, BWN_GPIO_CONTROL);
|
|
|
|
val = bwn_led_onoff(led, val, 1);
|
|
|
|
BWN_WRITE_2(mac, BWN_GPIO_CONTROL, val);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2010-03-09 19:58:00 +00:00
|
|
|
if (led->led_flags & BWN_LED_F_SLOW) {
|
|
|
|
BWN_LED_SLOWDOWN(on_dur);
|
|
|
|
BWN_LED_SLOWDOWN(off_dur);
|
|
|
|
}
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2010-03-09 19:58:00 +00:00
|
|
|
sc->sc_led_blinking = 1;
|
|
|
|
sc->sc_led_blink_offdur = off_dur;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2010-03-09 19:58:00 +00:00
|
|
|
callout_reset(&sc->sc_led_blink_ch, on_dur, bwn_led_blink_next, mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_led_blink_next(void *arg)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = arg;
|
2010-03-09 19:58:00 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
|
|
|
uint16_t val;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2010-03-09 19:58:00 +00:00
|
|
|
val = BWN_READ_2(mac, BWN_GPIO_CONTROL);
|
|
|
|
val = bwn_led_onoff(sc->sc_blink_led, val, 0);
|
|
|
|
BWN_WRITE_2(mac, BWN_GPIO_CONTROL, val);
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2010-03-09 19:58:00 +00:00
|
|
|
callout_reset(&sc->sc_led_blink_ch, sc->sc_led_blink_offdur,
|
|
|
|
bwn_led_blink_end, mac);
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_led_blink_end(void *arg)
|
|
|
|
{
|
|
|
|
struct bwn_mac *mac = arg;
|
2010-03-09 19:58:00 +00:00
|
|
|
struct bwn_softc *sc = mac->mac_sc;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
2010-03-09 19:58:00 +00:00
|
|
|
sc->sc_led_blinking = 0;
|
2010-02-16 01:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_suspend(device_t dev)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = device_get_softc(dev);
|
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
BWN_LOCK(sc);
|
|
|
|
bwn_stop(sc);
|
|
|
|
BWN_UNLOCK(sc);
|
2010-02-16 01:44:14 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bwn_resume(device_t dev)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = device_get_softc(dev);
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
int error = EDOOFUS;
|
2010-02-16 01:44:14 +00:00
|
|
|
|
Replay r286410. Change KPI of how device drivers that provide wireless
connectivity interact with the net80211 stack.
Historical background: originally wireless devices created an interface,
just like Ethernet devices do. Name of an interface matched the name of
the driver that created. Later, wlan(4) layer was introduced, and the
wlanX interfaces become the actual interface, leaving original ones as
"a parent interface" of wlanX. Kernelwise, the KPI between net80211 layer
and a driver became a mix of methods that pass a pointer to struct ifnet
as identifier and methods that pass pointer to struct ieee80211com. From
user point of view, the parent interface just hangs on in the ifconfig
list, and user can't do anything useful with it.
Now, the struct ifnet goes away. The struct ieee80211com is the only
KPI between a device driver and net80211. Details:
- The struct ieee80211com is embedded into drivers softc.
- Packets are sent via new ic_transmit method, which is very much like
the previous if_transmit.
- Bringing parent up/down is done via new ic_parent method, which notifies
driver about any changes: number of wlan(4) interfaces, number of them
in promisc or allmulti state.
- Device specific ioctls (if any) are received on new ic_ioctl method.
- Packets/errors accounting are done by the stack. In certain cases, when
driver experiences errors and can not attribute them to any specific
interface, driver updates ic_oerrors or ic_ierrors counters.
Details on interface configuration with new world order:
- A sequence of commands needed to bring up wireless DOESN"T change.
- /etc/rc.conf parameters DON'T change.
- List of devices that can be used to create wlan(4) interfaces is
now provided by net.wlan.devices sysctl.
Most drivers in this change were converted by me, except of wpi(4),
that was done by Andriy Voskoboinyk. Big thanks to Kevin Lo for testing
changes to at least 8 drivers. Thanks to pluknet@, Oliver Hartmann,
Olivier Cochard, gjb@, mmoll@, op@ and lev@, who also participated in
testing.
Reviewed by: adrian
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
2015-08-27 08:56:39 +00:00
|
|
|
BWN_LOCK(sc);
|
|
|
|
if (sc->sc_ic.ic_nrunning > 0)
|
|
|
|
error = bwn_init(sc);
|
|
|
|
BWN_UNLOCK(sc);
|
|
|
|
if (error == 0)
|
|
|
|
ieee80211_start_all(&sc->sc_ic);
|
2010-02-16 01:44:14 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bwn_rfswitch(void *arg)
|
|
|
|
{
|
|
|
|
struct bwn_softc *sc = arg;
|
|
|
|
struct bwn_mac *mac = sc->sc_curmac;
|
|
|
|
int cur = 0, prev = 0;
|
|
|
|
|
|
|
|
KASSERT(mac->mac_status >= BWN_MAC_STATUS_STARTED,
|
|
|
|
("%s: invalid MAC status %d", __func__, mac->mac_status));
|
|
|
|
|
2016-05-14 23:44:30 +00:00
|
|
|
if (mac->mac_phy.rev >= 3 || mac->mac_phy.type == BWN_PHYTYPE_LP
|
|
|
|
|| mac->mac_phy.type == BWN_PHYTYPE_N) {
|
2010-02-16 01:44:14 +00:00
|
|
|
if (!(BWN_READ_4(mac, BWN_RF_HWENABLED_HI)
|
|
|
|
& BWN_RF_HWENABLED_HI_MASK))
|
|
|
|
cur = 1;
|
|
|
|
} else {
|
|
|
|
if (BWN_READ_2(mac, BWN_RF_HWENABLED_LO)
|
|
|
|
& BWN_RF_HWENABLED_LO_MASK)
|
|
|
|
cur = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mac->mac_flags & BWN_MAC_FLAG_RADIO_ON)
|
|
|
|
prev = 1;
|
|
|
|
|
2016-05-14 23:44:30 +00:00
|
|
|
DPRINTF(sc, BWN_DEBUG_RESET, "%s: called; cur=%d, prev=%d\n",
|
|
|
|
__func__, cur, prev);
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
if (cur != prev) {
|
|
|
|
if (cur)
|
|
|
|
mac->mac_flags |= BWN_MAC_FLAG_RADIO_ON;
|
|
|
|
else
|
|
|
|
mac->mac_flags &= ~BWN_MAC_FLAG_RADIO_ON;
|
|
|
|
|
|
|
|
device_printf(sc->sc_dev,
|
|
|
|
"status of RF switch is changed to %s\n",
|
|
|
|
cur ? "ON" : "OFF");
|
|
|
|
if (cur != mac->mac_phy.rf_on) {
|
|
|
|
if (cur)
|
|
|
|
bwn_rf_turnon(mac);
|
|
|
|
else
|
|
|
|
bwn_rf_turnoff(mac);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
callout_schedule(&sc->sc_rfswitch_ch, hz);
|
|
|
|
}
|
|
|
|
|
2010-02-23 19:55:54 +00:00
|
|
|
static void
|
|
|
|
bwn_sysctl_node(struct bwn_softc *sc)
|
|
|
|
{
|
|
|
|
device_t dev = sc->sc_dev;
|
|
|
|
struct bwn_mac *mac;
|
|
|
|
struct bwn_stats *stats;
|
|
|
|
|
|
|
|
/* XXX assume that count of MAC is only 1. */
|
|
|
|
|
|
|
|
if ((mac = sc->sc_curmac) == NULL)
|
|
|
|
return;
|
|
|
|
stats = &mac->mac_stats;
|
|
|
|
|
2011-01-12 19:53:56 +00:00
|
|
|
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
|
2010-02-23 19:55:54 +00:00
|
|
|
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
|
|
|
"linknoise", CTLFLAG_RW, &stats->rts, 0, "Noise level");
|
2011-01-12 19:53:56 +00:00
|
|
|
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
|
2010-02-23 19:55:54 +00:00
|
|
|
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
|
|
|
"rts", CTLFLAG_RW, &stats->rts, 0, "RTS");
|
2011-01-12 19:53:56 +00:00
|
|
|
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
|
2010-02-23 19:55:54 +00:00
|
|
|
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
|
|
|
"rtsfail", CTLFLAG_RW, &stats->rtsfail, 0, "RTS failed to send");
|
|
|
|
|
|
|
|
#ifdef BWN_DEBUG
|
|
|
|
SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
|
|
|
|
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
|
|
|
"debug", CTLFLAG_RW, &sc->sc_debug, 0, "Debug flags");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:44:14 +00:00
|
|
|
static device_method_t bwn_methods[] = {
|
|
|
|
/* Device interface */
|
|
|
|
DEVMETHOD(device_probe, bwn_probe),
|
|
|
|
DEVMETHOD(device_attach, bwn_attach),
|
|
|
|
DEVMETHOD(device_detach, bwn_detach),
|
|
|
|
DEVMETHOD(device_suspend, bwn_suspend),
|
|
|
|
DEVMETHOD(device_resume, bwn_resume),
|
2011-11-22 21:55:40 +00:00
|
|
|
DEVMETHOD_END
|
2010-02-16 01:44:14 +00:00
|
|
|
};
|
2018-02-05 23:38:15 +00:00
|
|
|
static driver_t bwn_driver = {
|
2010-02-16 01:44:14 +00:00
|
|
|
"bwn",
|
|
|
|
bwn_methods,
|
|
|
|
sizeof(struct bwn_softc)
|
|
|
|
};
|
|
|
|
static devclass_t bwn_devclass;
|
2018-02-05 23:38:15 +00:00
|
|
|
DRIVER_MODULE(bwn, bhnd, bwn_driver, bwn_devclass, 0, 0);
|
|
|
|
MODULE_DEPEND(bwn, bhnd, 1, 1, 1);
|
Introduce bwn(4) support for the bhnd(4) bus.
Currently, bwn(4) relies on the siba_bwn(4) bus driver to provide support
for the on-chip SSB interconnect found in Broadcom's older PCI(e) Wi-Fi
adapters. Non-PCI Wi-Fi adapters, as well as the newer BCMA interconnect
found in post-2009 Broadcom Wi-Fi hardware, are not supported by
siba_bwn(4).
The bhnd(4) bus driver (also used by the FreeBSD/MIPS Broadcom port)
provides a unified kernel interface to a superset of the hardware supported
by siba_bwn; by attaching bwn(4) via bhnd(4), we can support both modern
PCI(e) Wi-Fi devices based on the BCMA backplane interconnect, as well as
Broadcom MIPS WiSoCs that include a D11 MAC core directly attached to their
SSB or BCMA backplane.
This diff introduces opt-in bwn(4) support for bhnd(4) by providing:
- A small bwn(4) driver subclass, if_bwn_bhnd, that attaches via
bhnd(4) instead of siba_bwn(4).
- A bhndb(4)-based PCI host bridge driver, if_bwn_pci, that optionally
probes at a higher priority than the siba_bwn(4) PCI driver.
- A set of compatibility shims that perform translation of bwn(4)'s
siba_bwn function calls into their bhnd(9) API equivalents when bwn(4)
is attached via a bhnd(4) bus parent. When bwn(4) is attached via
siba_bwn(4), all siba_bwn function calls are simply passed through to
their original implementations.
To test bwn(4) with bhnd(4), place the following lines in loader.conf(5):
hw.bwn_pci.preferred="1"
if_bwn_pci_load="YES
bwn_v4_ucode_load="YES"
bwn_v4_lp_ucode_load="YES"
To verify that bwn(4) is using bhnd(4), you can check dmesg:
bwn0: <Broadcom 802.11 MAC/PHY/Radio, rev 15> ... on bhnd0
... or devinfo(8):
pcib2
pci2
bwn_pci0
bhndb0
bhnd0
bwn0
...
bwn(4)/bhnd(4) has been tested for regressions with most chipsets currently
supported by bwn(4), including:
- BCM4312
- BCM4318
- BCM4321
With minimal changes to the DMA code (not included in this commit), I was
also able to test support for newer BCMA devices by bringing up basic
working Wi-Fi on two previously unsupported, BCMA-based N-PHY chipsets:
- BCM43224
- BCM43225
Approved by: adrian (mentor, implicit)
Sponsored by: The FreeBSD Foundation & Plausible Labs
Differential Revision: https://reviews.freebsd.org/D13041
2017-12-02 02:21:27 +00:00
|
|
|
MODULE_DEPEND(bwn, gpiobus, 1, 1, 1);
|
2010-02-16 01:44:14 +00:00
|
|
|
MODULE_DEPEND(bwn, wlan, 1, 1, 1); /* 802.11 media layer */
|
|
|
|
MODULE_DEPEND(bwn, firmware, 1, 1, 1); /* firmware support */
|
|
|
|
MODULE_DEPEND(bwn, wlan_amrr, 1, 1, 1);
|
[bwn] [bhnd] initial support for using bhnd for if_bwn devices.
This is an initial work in progress to use the replacement bhnd
bus code for devices which support it.
* Add manpage updates for bhnd, bhndb, siba
* Add kernel options for bhnd, bhndbus, etc
* Add initial support in if_bwn_pci / if_bwn_mac for using bhnd
as the bus transport for suppoted NICs
* if_bwn_pci will eventually be the PCI bus glue to interface to bwn,
which will use the right backend bus to attach to, versus direct
nexus/bhnd attachments (as found in embedded broadcom devices.)
The PCI glue defaults to probing at a lower level than the bwn glue,
so bwn should still attach as per normal without a boot time tunable set.
It's also not fully fleshed out - the bwn probe/attach code needs to be
broken out into platform and bus specific things (just like ath, ath_pci,
ath_ahb) before we can shift the driver over to using this.
Tested:
* BCM4311, STA mode
* BCM4312, STA mode
Submitted by: Landon Fuller <landonf@landonf.org>
Differential Revision: https://reviews.freebsd.org/D6191
2016-05-04 23:38:27 +00:00
|
|
|
MODULE_VERSION(bwn, 1);
|