14db7dd1bSJoe Talbott /*-
24db7dd1bSJoe Talbott * Copyright (c) 2006,2007
34db7dd1bSJoe Talbott * Damien Bergamini <damien.bergamini@free.fr>
44db7dd1bSJoe Talbott * Benjamin Close <Benjamin.Close@clearchain.com>
57370bffcSMatthew Dillon * Copyright (c) 2015 Andriy Voskoboinyk <avos@FreeBSD.org>
64db7dd1bSJoe Talbott *
74db7dd1bSJoe Talbott * Permission to use, copy, modify, and distribute this software for any
84db7dd1bSJoe Talbott * purpose with or without fee is hereby granted, provided that the above
94db7dd1bSJoe Talbott * copyright notice and this permission notice appear in all copies.
104db7dd1bSJoe Talbott *
114db7dd1bSJoe Talbott * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
124db7dd1bSJoe Talbott * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
134db7dd1bSJoe Talbott * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
144db7dd1bSJoe Talbott * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
154db7dd1bSJoe Talbott * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
164db7dd1bSJoe Talbott * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
174db7dd1bSJoe Talbott * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
184db7dd1bSJoe Talbott */
194db7dd1bSJoe Talbott
20d0c50e91SJohannes Hofmann #include <sys/cdefs.h>
21d0c50e91SJohannes Hofmann __FBSDID("$FreeBSD$");
22d0c50e91SJohannes Hofmann
234db7dd1bSJoe Talbott /*
244db7dd1bSJoe Talbott * Driver for Intel PRO/Wireless 3945ABG 802.11 network adapters.
254db7dd1bSJoe Talbott *
264db7dd1bSJoe Talbott * The 3945ABG network adapter doesn't use traditional hardware as
274db7dd1bSJoe Talbott * many other adaptors do. Instead at run time the eeprom is set into a known
284db7dd1bSJoe Talbott * state and told to load boot firmware. The boot firmware loads an init and a
294db7dd1bSJoe Talbott * main binary firmware image into SRAM on the card via DMA.
304db7dd1bSJoe Talbott * Once the firmware is loaded, the driver/hw then
31d0c50e91SJohannes Hofmann * communicate by way of circular dma rings via the SRAM to the firmware.
324db7dd1bSJoe Talbott *
334db7dd1bSJoe Talbott * There is 6 memory rings. 1 command ring, 1 rx data ring & 4 tx data rings.
344db7dd1bSJoe Talbott * The 4 tx data rings allow for prioritization QoS.
354db7dd1bSJoe Talbott *
364db7dd1bSJoe Talbott * The rx data ring consists of 32 dma buffers. Two registers are used to
374db7dd1bSJoe Talbott * indicate where in the ring the driver and the firmware are up to. The
384db7dd1bSJoe Talbott * driver sets the initial read index (reg1) and the initial write index (reg2),
394db7dd1bSJoe Talbott * the firmware updates the read index (reg1) on rx of a packet and fires an
404db7dd1bSJoe Talbott * interrupt. The driver then processes the buffers starting at reg1 indicating
414db7dd1bSJoe Talbott * to the firmware which buffers have been accessed by updating reg2. At the
424db7dd1bSJoe Talbott * same time allocating new memory for the processed buffer.
434db7dd1bSJoe Talbott *
444db7dd1bSJoe Talbott * A similar thing happens with the tx rings. The difference is the firmware
454db7dd1bSJoe Talbott * stop processing buffers once the queue is full and until confirmation
467370bffcSMatthew Dillon * of a successful transmition (tx_done) has occurred.
474db7dd1bSJoe Talbott *
484db7dd1bSJoe Talbott * The command ring operates in the same manner as the tx queues.
494db7dd1bSJoe Talbott *
504db7dd1bSJoe Talbott * All communication direct to the card (ie eeprom) is classed as Stage1
514db7dd1bSJoe Talbott * communication
524db7dd1bSJoe Talbott *
534db7dd1bSJoe Talbott * All communication via the firmware to the card is classed as State2.
544db7dd1bSJoe Talbott * The firmware consists of 2 parts. A bootstrap firmware and a runtime
554db7dd1bSJoe Talbott * firmware. The bootstrap firmware and runtime firmware are loaded
564db7dd1bSJoe Talbott * from host memory via dma to the card then told to execute. From this point
574db7dd1bSJoe Talbott * on the majority of communications between the driver and the card goes
584db7dd1bSJoe Talbott * via the firmware.
594db7dd1bSJoe Talbott */
604db7dd1bSJoe Talbott
61d0c50e91SJohannes Hofmann #include "opt_wlan.h"
627370bffcSMatthew Dillon #include "opt_wpi.h"
63d0c50e91SJohannes Hofmann
644db7dd1bSJoe Talbott #include <sys/param.h>
654db7dd1bSJoe Talbott #include <sys/sysctl.h>
664db7dd1bSJoe Talbott #include <sys/sockio.h>
674db7dd1bSJoe Talbott #include <sys/mbuf.h>
684db7dd1bSJoe Talbott #include <sys/kernel.h>
694db7dd1bSJoe Talbott #include <sys/socket.h>
704db7dd1bSJoe Talbott #include <sys/systm.h>
714db7dd1bSJoe Talbott #include <sys/malloc.h>
724db7dd1bSJoe Talbott #include <sys/queue.h>
734db7dd1bSJoe Talbott #include <sys/taskqueue.h>
744db7dd1bSJoe Talbott #include <sys/module.h>
754db7dd1bSJoe Talbott #include <sys/bus.h>
764db7dd1bSJoe Talbott #include <sys/endian.h>
774db7dd1bSJoe Talbott #include <sys/linker.h>
784db7dd1bSJoe Talbott #include <sys/firmware.h>
794db7dd1bSJoe Talbott
807370bffcSMatthew Dillon #if defined(__DragonFly__)
817370bffcSMatthew Dillon /* empty */
827370bffcSMatthew Dillon #else
837370bffcSMatthew Dillon #include <machine/bus.h>
847370bffcSMatthew Dillon #include <machine/resource.h>
857370bffcSMatthew Dillon #endif
864db7dd1bSJoe Talbott #include <sys/rman.h>
874db7dd1bSJoe Talbott
884db7dd1bSJoe Talbott #include <bus/pci/pcireg.h>
894db7dd1bSJoe Talbott #include <bus/pci/pcivar.h>
904db7dd1bSJoe Talbott
914db7dd1bSJoe Talbott #include <net/bpf.h>
924db7dd1bSJoe Talbott #include <net/if.h>
93d0c50e91SJohannes Hofmann #include <net/if_var.h>
944db7dd1bSJoe Talbott #include <net/if_arp.h>
954db7dd1bSJoe Talbott #include <net/ethernet.h>
964db7dd1bSJoe Talbott #include <net/if_dl.h>
974db7dd1bSJoe Talbott #include <net/if_media.h>
984db7dd1bSJoe Talbott #include <net/if_types.h>
997370bffcSMatthew Dillon
1007370bffcSMatthew Dillon #include <netinet/in.h>
1017370bffcSMatthew Dillon #include <netinet/in_systm.h>
1027370bffcSMatthew Dillon #include <netinet/in_var.h>
1037370bffcSMatthew Dillon #include <netinet/if_ether.h>
1047370bffcSMatthew Dillon #include <netinet/ip.h>
1054db7dd1bSJoe Talbott
1064db7dd1bSJoe Talbott #include <netproto/802_11/ieee80211_var.h>
1074db7dd1bSJoe Talbott #include <netproto/802_11/ieee80211_radiotap.h>
1084db7dd1bSJoe Talbott #include <netproto/802_11/ieee80211_regdomain.h>
1094db7dd1bSJoe Talbott #include <netproto/802_11/ieee80211_ratectl.h>
1104db7dd1bSJoe Talbott
111d0c50e91SJohannes Hofmann #include <dev/netif/wpi/if_wpireg.h>
112d0c50e91SJohannes Hofmann #include <dev/netif/wpi/if_wpivar.h>
1137370bffcSMatthew Dillon #include <dev/netif/wpi/if_wpi_debug.h>
1144db7dd1bSJoe Talbott
1154db7dd1bSJoe Talbott struct wpi_ident {
1164db7dd1bSJoe Talbott uint16_t vendor;
1174db7dd1bSJoe Talbott uint16_t device;
1184db7dd1bSJoe Talbott uint16_t subdevice;
1194db7dd1bSJoe Talbott const char *name;
1204db7dd1bSJoe Talbott };
1214db7dd1bSJoe Talbott
1224db7dd1bSJoe Talbott static const struct wpi_ident wpi_ident_table[] = {
1234db7dd1bSJoe Talbott /* The below entries support ABG regardless of the subid */
1244db7dd1bSJoe Talbott { 0x8086, 0x4222, 0x0, "Intel(R) PRO/Wireless 3945ABG" },
1254db7dd1bSJoe Talbott { 0x8086, 0x4227, 0x0, "Intel(R) PRO/Wireless 3945ABG" },
1264db7dd1bSJoe Talbott /* The below entries only support BG */
1274db7dd1bSJoe Talbott { 0x8086, 0x4222, 0x1005, "Intel(R) PRO/Wireless 3945BG" },
1284db7dd1bSJoe Talbott { 0x8086, 0x4222, 0x1034, "Intel(R) PRO/Wireless 3945BG" },
1294db7dd1bSJoe Talbott { 0x8086, 0x4227, 0x1014, "Intel(R) PRO/Wireless 3945BG" },
1304db7dd1bSJoe Talbott { 0x8086, 0x4222, 0x1044, "Intel(R) PRO/Wireless 3945BG" },
1314db7dd1bSJoe Talbott { 0, 0, 0, NULL }
1324db7dd1bSJoe Talbott };
1334db7dd1bSJoe Talbott
1347370bffcSMatthew Dillon static int wpi_probe(device_t);
1357370bffcSMatthew Dillon static int wpi_attach(device_t);
1367370bffcSMatthew Dillon static void wpi_radiotap_attach(struct wpi_softc *);
1377370bffcSMatthew Dillon static void wpi_sysctlattach(struct wpi_softc *);
1387370bffcSMatthew Dillon static void wpi_init_beacon(struct wpi_vap *);
1394db7dd1bSJoe Talbott static struct ieee80211vap *wpi_vap_create(struct ieee80211com *,
140d0c50e91SJohannes Hofmann const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
141d0c50e91SJohannes Hofmann const uint8_t [IEEE80211_ADDR_LEN],
142d0c50e91SJohannes Hofmann const uint8_t [IEEE80211_ADDR_LEN]);
1434db7dd1bSJoe Talbott static void wpi_vap_delete(struct ieee80211vap *);
1447370bffcSMatthew Dillon static int wpi_detach(device_t);
1457370bffcSMatthew Dillon static int wpi_shutdown(device_t);
1467370bffcSMatthew Dillon static int wpi_suspend(device_t);
1477370bffcSMatthew Dillon static int wpi_resume(device_t);
1487370bffcSMatthew Dillon static int wpi_nic_lock(struct wpi_softc *);
1497370bffcSMatthew Dillon static int wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int);
1504db7dd1bSJoe Talbott static void wpi_dma_map_addr(void *, bus_dma_segment_t *, int, int);
1517370bffcSMatthew Dillon static int wpi_dma_contig_alloc(struct wpi_softc *, struct wpi_dma_info *,
1527370bffcSMatthew Dillon void **, bus_size_t, bus_size_t);
1537370bffcSMatthew Dillon static void wpi_dma_contig_free(struct wpi_dma_info *);
1544db7dd1bSJoe Talbott static int wpi_alloc_shared(struct wpi_softc *);
1554db7dd1bSJoe Talbott static void wpi_free_shared(struct wpi_softc *);
1564db7dd1bSJoe Talbott static int wpi_alloc_fwmem(struct wpi_softc *);
1574db7dd1bSJoe Talbott static void wpi_free_fwmem(struct wpi_softc *);
1587370bffcSMatthew Dillon static int wpi_alloc_rx_ring(struct wpi_softc *);
1597370bffcSMatthew Dillon static void wpi_update_rx_ring(struct wpi_softc *);
1607370bffcSMatthew Dillon static void wpi_update_rx_ring_ps(struct wpi_softc *);
1617370bffcSMatthew Dillon static void wpi_reset_rx_ring(struct wpi_softc *);
1627370bffcSMatthew Dillon static void wpi_free_rx_ring(struct wpi_softc *);
1637370bffcSMatthew Dillon static int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *,
1647370bffcSMatthew Dillon uint8_t);
1657370bffcSMatthew Dillon static void wpi_update_tx_ring(struct wpi_softc *, struct wpi_tx_ring *);
1667370bffcSMatthew Dillon static void wpi_update_tx_ring_ps(struct wpi_softc *,
1677370bffcSMatthew Dillon struct wpi_tx_ring *);
1687370bffcSMatthew Dillon static void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *);
1697370bffcSMatthew Dillon static void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *);
1707370bffcSMatthew Dillon static int wpi_read_eeprom(struct wpi_softc *,
1717370bffcSMatthew Dillon uint8_t macaddr[IEEE80211_ADDR_LEN]);
1727370bffcSMatthew Dillon static uint32_t wpi_eeprom_channel_flags(struct wpi_eeprom_chan *);
1737370bffcSMatthew Dillon static void wpi_read_eeprom_band(struct wpi_softc *, uint8_t, int, int *,
1747370bffcSMatthew Dillon struct ieee80211_channel[]);
1757370bffcSMatthew Dillon static int wpi_read_eeprom_channels(struct wpi_softc *, uint8_t);
1767370bffcSMatthew Dillon static struct wpi_eeprom_chan *wpi_find_eeprom_channel(struct wpi_softc *,
1777370bffcSMatthew Dillon struct ieee80211_channel *);
1787370bffcSMatthew Dillon static void wpi_getradiocaps(struct ieee80211com *, int, int *,
1797370bffcSMatthew Dillon struct ieee80211_channel[]);
1807370bffcSMatthew Dillon static int wpi_setregdomain(struct ieee80211com *,
1817370bffcSMatthew Dillon struct ieee80211_regdomain *, int,
1827370bffcSMatthew Dillon struct ieee80211_channel[]);
1837370bffcSMatthew Dillon static int wpi_read_eeprom_group(struct wpi_softc *, uint8_t);
1847370bffcSMatthew Dillon static struct ieee80211_node *wpi_node_alloc(struct ieee80211vap *,
1857370bffcSMatthew Dillon const uint8_t mac[IEEE80211_ADDR_LEN]);
1867370bffcSMatthew Dillon static void wpi_node_free(struct ieee80211_node *);
1877370bffcSMatthew Dillon static void wpi_ibss_recv_mgmt(struct ieee80211_node *, struct mbuf *, int,
1887370bffcSMatthew Dillon const struct ieee80211_rx_stats *,
1897370bffcSMatthew Dillon int, int);
1907370bffcSMatthew Dillon static void wpi_restore_node(void *, struct ieee80211_node *);
1917370bffcSMatthew Dillon static void wpi_restore_node_table(struct wpi_softc *, struct wpi_vap *);
1927370bffcSMatthew Dillon static int wpi_newstate(struct ieee80211vap *, enum ieee80211_state, int);
1937370bffcSMatthew Dillon static void wpi_calib_timeout(void *);
1947370bffcSMatthew Dillon static void wpi_rx_done(struct wpi_softc *, struct wpi_rx_desc *,
1954db7dd1bSJoe Talbott struct wpi_rx_data *);
1967370bffcSMatthew Dillon static void wpi_rx_statistics(struct wpi_softc *, struct wpi_rx_desc *,
1977370bffcSMatthew Dillon struct wpi_rx_data *);
1987370bffcSMatthew Dillon static void wpi_tx_done(struct wpi_softc *, struct wpi_rx_desc *);
1997370bffcSMatthew Dillon static void wpi_cmd_done(struct wpi_softc *, struct wpi_rx_desc *);
2004db7dd1bSJoe Talbott static void wpi_notif_intr(struct wpi_softc *);
2017370bffcSMatthew Dillon static void wpi_wakeup_intr(struct wpi_softc *);
2027370bffcSMatthew Dillon #ifdef WPI_DEBUG
2037370bffcSMatthew Dillon static void wpi_debug_registers(struct wpi_softc *);
2047370bffcSMatthew Dillon #endif
2057370bffcSMatthew Dillon static void wpi_fatal_intr(struct wpi_softc *);
2064db7dd1bSJoe Talbott static void wpi_intr(void *);
2077370bffcSMatthew Dillon static void wpi_free_txfrags(struct wpi_softc *, uint16_t);
2087370bffcSMatthew Dillon static int wpi_cmd2(struct wpi_softc *, struct wpi_buf *);
2094db7dd1bSJoe Talbott static int wpi_tx_data(struct wpi_softc *, struct mbuf *,
2107370bffcSMatthew Dillon struct ieee80211_node *);
2117370bffcSMatthew Dillon static int wpi_tx_data_raw(struct wpi_softc *, struct mbuf *,
2127370bffcSMatthew Dillon struct ieee80211_node *,
2137370bffcSMatthew Dillon const struct ieee80211_bpf_params *);
2144db7dd1bSJoe Talbott static int wpi_raw_xmit(struct ieee80211_node *, struct mbuf *,
2154db7dd1bSJoe Talbott const struct ieee80211_bpf_params *);
2167370bffcSMatthew Dillon static int wpi_transmit(struct ieee80211com *, struct mbuf *);
2177370bffcSMatthew Dillon static void wpi_watchdog_rfkill(void *);
2187370bffcSMatthew Dillon static void wpi_scan_timeout(void *);
2197370bffcSMatthew Dillon static void wpi_tx_timeout(void *);
2207370bffcSMatthew Dillon static void wpi_parent(struct ieee80211com *);
2217370bffcSMatthew Dillon static int wpi_cmd(struct wpi_softc *, uint8_t, const void *, uint16_t,
2227370bffcSMatthew Dillon int);
2237370bffcSMatthew Dillon static int wpi_mrr_setup(struct wpi_softc *);
2247370bffcSMatthew Dillon static int wpi_add_node(struct wpi_softc *, struct ieee80211_node *);
2257370bffcSMatthew Dillon static int wpi_add_broadcast_node(struct wpi_softc *, int);
2267370bffcSMatthew Dillon static int wpi_add_ibss_node(struct wpi_softc *, struct ieee80211_node *);
2277370bffcSMatthew Dillon static void wpi_del_node(struct wpi_softc *, struct ieee80211_node *);
2287370bffcSMatthew Dillon static int wpi_updateedca(struct ieee80211com *);
2297370bffcSMatthew Dillon static void wpi_set_promisc(struct wpi_softc *);
2307370bffcSMatthew Dillon static void wpi_update_promisc(struct ieee80211com *);
2317370bffcSMatthew Dillon static void wpi_update_mcast(struct ieee80211com *);
2327370bffcSMatthew Dillon static void wpi_set_led(struct wpi_softc *, uint8_t, uint8_t, uint8_t);
2337370bffcSMatthew Dillon static int wpi_set_timing(struct wpi_softc *, struct ieee80211_node *);
2347370bffcSMatthew Dillon static void wpi_power_calibration(struct wpi_softc *);
2357370bffcSMatthew Dillon static int wpi_set_txpower(struct wpi_softc *, int);
2367370bffcSMatthew Dillon static int wpi_get_power_index(struct wpi_softc *,
2377370bffcSMatthew Dillon struct wpi_power_group *, uint8_t, int, int);
2387370bffcSMatthew Dillon static int wpi_set_pslevel(struct wpi_softc *, uint8_t, int, int);
2397370bffcSMatthew Dillon static int wpi_send_btcoex(struct wpi_softc *);
2407370bffcSMatthew Dillon static int wpi_send_rxon(struct wpi_softc *, int, int);
2417370bffcSMatthew Dillon static int wpi_config(struct wpi_softc *);
2427370bffcSMatthew Dillon static uint16_t wpi_get_active_dwell_time(struct wpi_softc *,
2437370bffcSMatthew Dillon struct ieee80211_channel *, uint8_t);
2447370bffcSMatthew Dillon static uint16_t wpi_limit_dwell(struct wpi_softc *, uint16_t);
2457370bffcSMatthew Dillon static uint16_t wpi_get_passive_dwell_time(struct wpi_softc *,
2467370bffcSMatthew Dillon struct ieee80211_channel *);
2477370bffcSMatthew Dillon static uint32_t wpi_get_scan_pause_time(uint32_t, uint16_t);
2487370bffcSMatthew Dillon static int wpi_scan(struct wpi_softc *, struct ieee80211_channel *);
2497370bffcSMatthew Dillon static int wpi_auth(struct wpi_softc *, struct ieee80211vap *);
2507370bffcSMatthew Dillon static int wpi_config_beacon(struct wpi_vap *);
2517370bffcSMatthew Dillon static int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *);
2527370bffcSMatthew Dillon static void wpi_update_beacon(struct ieee80211vap *, int);
2537370bffcSMatthew Dillon static void wpi_newassoc(struct ieee80211_node *, int);
2547370bffcSMatthew Dillon static int wpi_run(struct wpi_softc *, struct ieee80211vap *);
2557370bffcSMatthew Dillon static int wpi_load_key(struct ieee80211_node *,
2567370bffcSMatthew Dillon const struct ieee80211_key *);
2577370bffcSMatthew Dillon static void wpi_load_key_cb(void *, struct ieee80211_node *);
2587370bffcSMatthew Dillon static int wpi_set_global_keys(struct ieee80211_node *);
2597370bffcSMatthew Dillon static int wpi_del_key(struct ieee80211_node *,
2607370bffcSMatthew Dillon const struct ieee80211_key *);
2617370bffcSMatthew Dillon static void wpi_del_key_cb(void *, struct ieee80211_node *);
2627370bffcSMatthew Dillon static int wpi_process_key(struct ieee80211vap *,
2637370bffcSMatthew Dillon const struct ieee80211_key *, int);
2647370bffcSMatthew Dillon static int wpi_key_set(struct ieee80211vap *,
2657370bffcSMatthew Dillon const struct ieee80211_key *);
2667370bffcSMatthew Dillon static int wpi_key_delete(struct ieee80211vap *,
2677370bffcSMatthew Dillon const struct ieee80211_key *);
2687370bffcSMatthew Dillon static int wpi_post_alive(struct wpi_softc *);
2697370bffcSMatthew Dillon static int wpi_load_bootcode(struct wpi_softc *, const uint8_t *,
2707370bffcSMatthew Dillon uint32_t);
2717370bffcSMatthew Dillon static int wpi_load_firmware(struct wpi_softc *);
2727370bffcSMatthew Dillon static int wpi_read_firmware(struct wpi_softc *);
2737370bffcSMatthew Dillon static void wpi_unload_firmware(struct wpi_softc *);
2747370bffcSMatthew Dillon static int wpi_clock_wait(struct wpi_softc *);
2757370bffcSMatthew Dillon static int wpi_apm_init(struct wpi_softc *);
2767370bffcSMatthew Dillon static void wpi_apm_stop_master(struct wpi_softc *);
2777370bffcSMatthew Dillon static void wpi_apm_stop(struct wpi_softc *);
2787370bffcSMatthew Dillon static void wpi_nic_config(struct wpi_softc *);
2797370bffcSMatthew Dillon static int wpi_hw_init(struct wpi_softc *);
2807370bffcSMatthew Dillon static void wpi_hw_stop(struct wpi_softc *);
2817370bffcSMatthew Dillon static void wpi_radio_on(void *, int);
2827370bffcSMatthew Dillon static void wpi_radio_off(void *, int);
2837370bffcSMatthew Dillon static int wpi_init(struct wpi_softc *);
2847370bffcSMatthew Dillon static void wpi_stop_locked(struct wpi_softc *);
2857370bffcSMatthew Dillon static void wpi_stop(struct wpi_softc *);
2864db7dd1bSJoe Talbott static void wpi_scan_start(struct ieee80211com *);
2874db7dd1bSJoe Talbott static void wpi_scan_end(struct ieee80211com *);
2884db7dd1bSJoe Talbott static void wpi_set_channel(struct ieee80211com *);
2894db7dd1bSJoe Talbott static void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long);
2904db7dd1bSJoe Talbott static void wpi_scan_mindwell(struct ieee80211_scan_state *);
2914db7dd1bSJoe Talbott
2924db7dd1bSJoe Talbott static device_method_t wpi_methods[] = {
2934db7dd1bSJoe Talbott /* Device interface */
2944db7dd1bSJoe Talbott DEVMETHOD(device_probe, wpi_probe),
2954db7dd1bSJoe Talbott DEVMETHOD(device_attach, wpi_attach),
2964db7dd1bSJoe Talbott DEVMETHOD(device_detach, wpi_detach),
2974db7dd1bSJoe Talbott DEVMETHOD(device_shutdown, wpi_shutdown),
2984db7dd1bSJoe Talbott DEVMETHOD(device_suspend, wpi_suspend),
2994db7dd1bSJoe Talbott DEVMETHOD(device_resume, wpi_resume),
3004db7dd1bSJoe Talbott
301d3c9c58eSSascha Wildner DEVMETHOD_END
3024db7dd1bSJoe Talbott };
3034db7dd1bSJoe Talbott
3044db7dd1bSJoe Talbott static driver_t wpi_driver = {
3054db7dd1bSJoe Talbott "wpi",
3064db7dd1bSJoe Talbott wpi_methods,
3074db7dd1bSJoe Talbott sizeof (struct wpi_softc)
3084db7dd1bSJoe Talbott };
3094db7dd1bSJoe Talbott static devclass_t wpi_devclass;
3104db7dd1bSJoe Talbott
311aa2b9d05SSascha Wildner DRIVER_MODULE(wpi, pci, wpi_driver, wpi_devclass, NULL, NULL);
3124db7dd1bSJoe Talbott
313d0c50e91SJohannes Hofmann MODULE_VERSION(wpi, 1);
314d0c50e91SJohannes Hofmann
3157370bffcSMatthew Dillon MODULE_DEPEND(wpi, pci, 1, 1, 1);
3167370bffcSMatthew Dillon MODULE_DEPEND(wpi, wlan, 1, 1, 1);
3177370bffcSMatthew Dillon MODULE_DEPEND(wpi, firmware, 1, 1, 1);
3184db7dd1bSJoe Talbott
3194db7dd1bSJoe Talbott static int
wpi_probe(device_t dev)3204db7dd1bSJoe Talbott wpi_probe(device_t dev)
3214db7dd1bSJoe Talbott {
3224db7dd1bSJoe Talbott const struct wpi_ident *ident;
3234db7dd1bSJoe Talbott
3244db7dd1bSJoe Talbott for (ident = wpi_ident_table; ident->name != NULL; ident++) {
3254db7dd1bSJoe Talbott if (pci_get_vendor(dev) == ident->vendor &&
3264db7dd1bSJoe Talbott pci_get_device(dev) == ident->device) {
3274db7dd1bSJoe Talbott device_set_desc(dev, ident->name);
328d0c50e91SJohannes Hofmann return (BUS_PROBE_DEFAULT);
3294db7dd1bSJoe Talbott }
3304db7dd1bSJoe Talbott }
3314db7dd1bSJoe Talbott return ENXIO;
3324db7dd1bSJoe Talbott }
3334db7dd1bSJoe Talbott
3344db7dd1bSJoe Talbott static int
wpi_attach(device_t dev)3354db7dd1bSJoe Talbott wpi_attach(device_t dev)
3364db7dd1bSJoe Talbott {
3377370bffcSMatthew Dillon struct wpi_softc *sc = (struct wpi_softc *)device_get_softc(dev);
3384db7dd1bSJoe Talbott struct ieee80211com *ic;
3397370bffcSMatthew Dillon uint8_t i;
3407370bffcSMatthew Dillon int error, rid;
3417370bffcSMatthew Dillon #ifdef WPI_DEBUG
3427370bffcSMatthew Dillon int supportsa = 1;
3434db7dd1bSJoe Talbott const struct wpi_ident *ident;
3447370bffcSMatthew Dillon #endif
3457370bffcSMatthew Dillon #if defined(__DragonFly__)
3467370bffcSMatthew Dillon int irq_flags;
3477370bffcSMatthew Dillon #endif
3484db7dd1bSJoe Talbott
3494db7dd1bSJoe Talbott sc->sc_dev = dev;
3504db7dd1bSJoe Talbott
3517370bffcSMatthew Dillon #ifdef WPI_DEBUG
3527370bffcSMatthew Dillon error = resource_int_value(device_get_name(sc->sc_dev),
3537370bffcSMatthew Dillon device_get_unit(sc->sc_dev), "debug", &(sc->sc_debug));
3547370bffcSMatthew Dillon if (error != 0)
3557370bffcSMatthew Dillon sc->sc_debug = 0;
3567370bffcSMatthew Dillon #else
3577370bffcSMatthew Dillon sc->sc_debug = 0;
3587370bffcSMatthew Dillon #endif
3597370bffcSMatthew Dillon
3607370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
3617370bffcSMatthew Dillon
3627370bffcSMatthew Dillon /*
3637370bffcSMatthew Dillon * Get the offset of the PCI Express Capability Structure in PCI
3647370bffcSMatthew Dillon * Configuration Space.
3657370bffcSMatthew Dillon */
3667370bffcSMatthew Dillon #if defined(__DragonFly__)
3677370bffcSMatthew Dillon error = pci_find_extcap(dev, PCIY_EXPRESS, &sc->sc_cap_off);
3687370bffcSMatthew Dillon #else
3697370bffcSMatthew Dillon error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off);
3707370bffcSMatthew Dillon #endif
3717370bffcSMatthew Dillon if (error != 0) {
3727370bffcSMatthew Dillon device_printf(dev, "PCIe capability structure not found!\n");
3737370bffcSMatthew Dillon return error;
3747370bffcSMatthew Dillon }
3754db7dd1bSJoe Talbott
3764db7dd1bSJoe Talbott /*
3774db7dd1bSJoe Talbott * Some card's only support 802.11b/g not a, check to see if
3784db7dd1bSJoe Talbott * this is one such card. A 0x0 in the subdevice table indicates
3794db7dd1bSJoe Talbott * the entire subdevice range is to be ignored.
3804db7dd1bSJoe Talbott */
3817370bffcSMatthew Dillon #ifdef WPI_DEBUG
3824db7dd1bSJoe Talbott for (ident = wpi_ident_table; ident->name != NULL; ident++) {
3834db7dd1bSJoe Talbott if (ident->subdevice &&
3844db7dd1bSJoe Talbott pci_get_subdevice(dev) == ident->subdevice) {
3854db7dd1bSJoe Talbott supportsa = 0;
3864db7dd1bSJoe Talbott break;
3874db7dd1bSJoe Talbott }
3884db7dd1bSJoe Talbott }
3897370bffcSMatthew Dillon #endif
3904db7dd1bSJoe Talbott
3917370bffcSMatthew Dillon /* Clear device-specific "PCI retry timeout" register (41h). */
3924db7dd1bSJoe Talbott pci_write_config(dev, 0x41, 0, 1);
3934db7dd1bSJoe Talbott
3947370bffcSMatthew Dillon /* Enable bus-mastering. */
3954db7dd1bSJoe Talbott pci_enable_busmaster(dev);
3964db7dd1bSJoe Talbott
397d0c50e91SJohannes Hofmann rid = PCIR_BAR(0);
398d0c50e91SJohannes Hofmann sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
3994db7dd1bSJoe Talbott RF_ACTIVE);
4004db7dd1bSJoe Talbott if (sc->mem == NULL) {
4017370bffcSMatthew Dillon device_printf(dev, "can't map mem space\n");
4027370bffcSMatthew Dillon return ENOMEM;
4034db7dd1bSJoe Talbott }
4044db7dd1bSJoe Talbott sc->sc_st = rman_get_bustag(sc->mem);
4054db7dd1bSJoe Talbott sc->sc_sh = rman_get_bushandle(sc->mem);
4064db7dd1bSJoe Talbott
4077370bffcSMatthew Dillon #if defined(__DragonFly__)
4087370bffcSMatthew Dillon pci_alloc_1intr(dev, 1, &rid, &irq_flags);
4097370bffcSMatthew Dillon sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, irq_flags);
4107370bffcSMatthew Dillon #else
4117370bffcSMatthew Dillon rid = 1;
4127370bffcSMatthew Dillon if (pci_alloc_msi(dev, &rid) == 0)
4137370bffcSMatthew Dillon rid = 1;
4147370bffcSMatthew Dillon else
415d0c50e91SJohannes Hofmann rid = 0;
4167370bffcSMatthew Dillon /* Install interrupt handler. */
4177370bffcSMatthew Dillon sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE |
4187370bffcSMatthew Dillon (rid != 0 ? 0 : RF_SHAREABLE));
4197370bffcSMatthew Dillon #endif
4204db7dd1bSJoe Talbott if (sc->irq == NULL) {
4217370bffcSMatthew Dillon device_printf(dev, "can't map interrupt\n");
4224db7dd1bSJoe Talbott error = ENOMEM;
4234db7dd1bSJoe Talbott goto fail;
4244db7dd1bSJoe Talbott }
4254db7dd1bSJoe Talbott
4267370bffcSMatthew Dillon WPI_LOCK_INIT(sc);
4277370bffcSMatthew Dillon WPI_TX_LOCK_INIT(sc);
4287370bffcSMatthew Dillon WPI_RXON_LOCK_INIT(sc);
4297370bffcSMatthew Dillon WPI_NT_LOCK_INIT(sc);
4307370bffcSMatthew Dillon WPI_TXQ_LOCK_INIT(sc);
4317370bffcSMatthew Dillon WPI_TXQ_STATE_LOCK_INIT(sc);
4327370bffcSMatthew Dillon
4337370bffcSMatthew Dillon /* Allocate DMA memory for firmware transfers. */
4344db7dd1bSJoe Talbott if ((error = wpi_alloc_fwmem(sc)) != 0) {
4357370bffcSMatthew Dillon device_printf(dev,
4367370bffcSMatthew Dillon "could not allocate memory for firmware, error %d\n",
4377370bffcSMatthew Dillon error);
4384db7dd1bSJoe Talbott goto fail;
4394db7dd1bSJoe Talbott }
4404db7dd1bSJoe Talbott
4417370bffcSMatthew Dillon /* Allocate shared page. */
4424db7dd1bSJoe Talbott if ((error = wpi_alloc_shared(sc)) != 0) {
4434db7dd1bSJoe Talbott device_printf(dev, "could not allocate shared page\n");
4444db7dd1bSJoe Talbott goto fail;
4454db7dd1bSJoe Talbott }
4464db7dd1bSJoe Talbott
4477370bffcSMatthew Dillon /* Allocate TX rings - 4 for QoS purposes, 1 for commands. */
4487370bffcSMatthew Dillon for (i = 0; i < WPI_DRV_NTXQUEUES; i++) {
4497370bffcSMatthew Dillon if ((error = wpi_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) {
4507370bffcSMatthew Dillon device_printf(dev,
4517370bffcSMatthew Dillon "could not allocate TX ring %d, error %d\n", i,
4527370bffcSMatthew Dillon error);
4534db7dd1bSJoe Talbott goto fail;
4544db7dd1bSJoe Talbott }
4554db7dd1bSJoe Talbott }
4564db7dd1bSJoe Talbott
4577370bffcSMatthew Dillon /* Allocate RX ring. */
4587370bffcSMatthew Dillon if ((error = wpi_alloc_rx_ring(sc)) != 0) {
4597370bffcSMatthew Dillon device_printf(dev, "could not allocate RX ring, error %d\n",
4607370bffcSMatthew Dillon error);
4614db7dd1bSJoe Talbott goto fail;
4624db7dd1bSJoe Talbott }
4634db7dd1bSJoe Talbott
4647370bffcSMatthew Dillon /* Clear pending interrupts. */
4657370bffcSMatthew Dillon WPI_WRITE(sc, WPI_INT, 0xffffffff);
4664db7dd1bSJoe Talbott
4677370bffcSMatthew Dillon ic = &sc->sc_ic;
46813f98998SMatthew Dillon ic->ic_softc = sc;
4694f898719SImre Vadász ic->ic_name = device_get_nameunit(dev);
4704db7dd1bSJoe Talbott ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
4714db7dd1bSJoe Talbott ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
4724db7dd1bSJoe Talbott
4737370bffcSMatthew Dillon /* Set device capabilities. */
4744db7dd1bSJoe Talbott ic->ic_caps =
4754db7dd1bSJoe Talbott IEEE80211_C_STA /* station mode supported */
4767370bffcSMatthew Dillon | IEEE80211_C_IBSS /* IBSS mode supported */
4777370bffcSMatthew Dillon | IEEE80211_C_HOSTAP /* Host access point mode */
4784db7dd1bSJoe Talbott | IEEE80211_C_MONITOR /* monitor mode supported */
4797370bffcSMatthew Dillon | IEEE80211_C_AHDEMO /* adhoc demo mode */
4807370bffcSMatthew Dillon | IEEE80211_C_BGSCAN /* capable of bg scanning */
4817370bffcSMatthew Dillon | IEEE80211_C_TXFRAG /* handle tx frags */
4824db7dd1bSJoe Talbott | IEEE80211_C_TXPMGT /* tx power management */
4834db7dd1bSJoe Talbott | IEEE80211_C_SHSLOT /* short slot time supported */
4844db7dd1bSJoe Talbott | IEEE80211_C_WPA /* 802.11i */
4857370bffcSMatthew Dillon | IEEE80211_C_SHPREAMBLE /* short preamble supported */
4864db7dd1bSJoe Talbott | IEEE80211_C_WME /* 802.11e */
4877370bffcSMatthew Dillon | IEEE80211_C_PMGT /* Station-side power mgmt */
4884db7dd1bSJoe Talbott ;
4894db7dd1bSJoe Talbott
4907370bffcSMatthew Dillon ic->ic_cryptocaps =
4917370bffcSMatthew Dillon IEEE80211_CRYPTO_AES_CCM;
4927370bffcSMatthew Dillon
4934db7dd1bSJoe Talbott /*
4944db7dd1bSJoe Talbott * Read in the eeprom and also setup the channels for
4954db7dd1bSJoe Talbott * net80211. We don't set the rates as net80211 does this for us
4964db7dd1bSJoe Talbott */
4977370bffcSMatthew Dillon if ((error = wpi_read_eeprom(sc, ic->ic_macaddr)) != 0) {
4987370bffcSMatthew Dillon device_printf(dev, "could not read EEPROM, error %d\n",
4997370bffcSMatthew Dillon error);
5007370bffcSMatthew Dillon goto fail;
5017370bffcSMatthew Dillon }
5024db7dd1bSJoe Talbott
5037370bffcSMatthew Dillon #ifdef WPI_DEBUG
5047370bffcSMatthew Dillon if (bootverbose) {
5057370bffcSMatthew Dillon device_printf(sc->sc_dev, "Regulatory Domain: %.4s\n",
5067370bffcSMatthew Dillon sc->domain);
5074db7dd1bSJoe Talbott device_printf(sc->sc_dev, "Hardware Type: %c\n",
5084db7dd1bSJoe Talbott sc->type > 1 ? 'B': '?');
5094db7dd1bSJoe Talbott device_printf(sc->sc_dev, "Hardware Revision: %c\n",
5107370bffcSMatthew Dillon ((sc->rev & 0xf0) == 0xd0) ? 'D': '?');
5114db7dd1bSJoe Talbott device_printf(sc->sc_dev, "SKU %s support 802.11a\n",
5124db7dd1bSJoe Talbott supportsa ? "does" : "does not");
5134db7dd1bSJoe Talbott
5147370bffcSMatthew Dillon /* XXX hw_config uses the PCIDEV for the Hardware rev. Must
5157370bffcSMatthew Dillon check what sc->rev really represents - benjsc 20070615 */
5164db7dd1bSJoe Talbott }
517c3d41318SSepherosa Ziehau #endif
5184db7dd1bSJoe Talbott
5197370bffcSMatthew Dillon ieee80211_ifattach(ic);
5207370bffcSMatthew Dillon ic->ic_vap_create = wpi_vap_create;
5217370bffcSMatthew Dillon ic->ic_vap_delete = wpi_vap_delete;
5227370bffcSMatthew Dillon ic->ic_parent = wpi_parent;
5234db7dd1bSJoe Talbott ic->ic_raw_xmit = wpi_raw_xmit;
5247370bffcSMatthew Dillon ic->ic_transmit = wpi_transmit;
5257370bffcSMatthew Dillon ic->ic_node_alloc = wpi_node_alloc;
5267370bffcSMatthew Dillon sc->sc_node_free = ic->ic_node_free;
5277370bffcSMatthew Dillon ic->ic_node_free = wpi_node_free;
5287370bffcSMatthew Dillon ic->ic_wme.wme_update = wpi_updateedca;
5297370bffcSMatthew Dillon ic->ic_update_promisc = wpi_update_promisc;
5307370bffcSMatthew Dillon ic->ic_update_mcast = wpi_update_mcast;
5317370bffcSMatthew Dillon ic->ic_newassoc = wpi_newassoc;
5324db7dd1bSJoe Talbott ic->ic_scan_start = wpi_scan_start;
5334db7dd1bSJoe Talbott ic->ic_scan_end = wpi_scan_end;
5344db7dd1bSJoe Talbott ic->ic_set_channel = wpi_set_channel;
5354db7dd1bSJoe Talbott ic->ic_scan_curchan = wpi_scan_curchan;
5364db7dd1bSJoe Talbott ic->ic_scan_mindwell = wpi_scan_mindwell;
5377370bffcSMatthew Dillon ic->ic_getradiocaps = wpi_getradiocaps;
5387370bffcSMatthew Dillon ic->ic_setregdomain = wpi_setregdomain;
5394db7dd1bSJoe Talbott
5407370bffcSMatthew Dillon sc->sc_update_rx_ring = wpi_update_rx_ring;
5417370bffcSMatthew Dillon sc->sc_update_tx_ring = wpi_update_tx_ring;
5424db7dd1bSJoe Talbott
5437370bffcSMatthew Dillon wpi_radiotap_attach(sc);
5447370bffcSMatthew Dillon
5457370bffcSMatthew Dillon callout_init_mtx(&sc->calib_to, &sc->rxon_mtx, 0);
5467370bffcSMatthew Dillon callout_init_mtx(&sc->scan_timeout, &sc->rxon_mtx, 0);
5477370bffcSMatthew Dillon callout_init_mtx(&sc->tx_timeout, &sc->txq_state_mtx, 0);
5487370bffcSMatthew Dillon callout_init_mtx(&sc->watchdog_rfkill, &sc->sc_mtx, 0);
5497370bffcSMatthew Dillon TASK_INIT(&sc->sc_radiooff_task, 0, wpi_radio_off, sc);
5507370bffcSMatthew Dillon TASK_INIT(&sc->sc_radioon_task, 0, wpi_radio_on, sc);
5517370bffcSMatthew Dillon
5527370bffcSMatthew Dillon wpi_sysctlattach(sc);
5534db7dd1bSJoe Talbott
5544db7dd1bSJoe Talbott /*
5554db7dd1bSJoe Talbott * Hook our interrupt after all initialization is complete.
5564db7dd1bSJoe Talbott */
557d0c50e91SJohannes Hofmann #if defined(__DragonFly__)
5584db7dd1bSJoe Talbott error = bus_setup_intr(dev, sc->irq, INTR_MPSAFE,
55981c64c9bSJoe Talbott wpi_intr, sc, &sc->sc_ih, &wlan_global_serializer);
560d0c50e91SJohannes Hofmann #else
561d0c50e91SJohannes Hofmann error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
562d0c50e91SJohannes Hofmann NULL, wpi_intr, sc, &sc->sc_ih);
563d0c50e91SJohannes Hofmann #endif
5644db7dd1bSJoe Talbott if (error != 0) {
5657370bffcSMatthew Dillon device_printf(dev, "can't establish interrupt, error %d\n",
5667370bffcSMatthew Dillon error);
5674db7dd1bSJoe Talbott goto fail;
5684db7dd1bSJoe Talbott }
5694db7dd1bSJoe Talbott
5704db7dd1bSJoe Talbott if (bootverbose)
5714db7dd1bSJoe Talbott ieee80211_announce(ic);
5727370bffcSMatthew Dillon
5737370bffcSMatthew Dillon #ifdef WPI_DEBUG
5747370bffcSMatthew Dillon if (sc->sc_debug & WPI_DEBUG_HW)
5754db7dd1bSJoe Talbott ieee80211_announce_channels(ic);
5764db7dd1bSJoe Talbott #endif
5777370bffcSMatthew Dillon
5787370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
5794db7dd1bSJoe Talbott return 0;
5804db7dd1bSJoe Talbott
581d0c50e91SJohannes Hofmann fail: wpi_detach(dev);
5827370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__);
5837370bffcSMatthew Dillon return error;
5844db7dd1bSJoe Talbott }
5854db7dd1bSJoe Talbott
5867370bffcSMatthew Dillon /*
5877370bffcSMatthew Dillon * Attach the interface to 802.11 radiotap.
5887370bffcSMatthew Dillon */
5897370bffcSMatthew Dillon static void
wpi_radiotap_attach(struct wpi_softc * sc)5907370bffcSMatthew Dillon wpi_radiotap_attach(struct wpi_softc *sc)
5914db7dd1bSJoe Talbott {
5927370bffcSMatthew Dillon struct wpi_rx_radiotap_header *rxtap = &sc->sc_rxtap;
5937370bffcSMatthew Dillon struct wpi_tx_radiotap_header *txtap = &sc->sc_txtap;
5944db7dd1bSJoe Talbott
5957370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
5967370bffcSMatthew Dillon ieee80211_radiotap_attach(&sc->sc_ic,
5977370bffcSMatthew Dillon &txtap->wt_ihdr, sizeof(*txtap), WPI_TX_RADIOTAP_PRESENT,
5987370bffcSMatthew Dillon &rxtap->wr_ihdr, sizeof(*rxtap), WPI_RX_RADIOTAP_PRESENT);
5997370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
6004db7dd1bSJoe Talbott }
6014db7dd1bSJoe Talbott
6027370bffcSMatthew Dillon static void
wpi_sysctlattach(struct wpi_softc * sc)6037370bffcSMatthew Dillon wpi_sysctlattach(struct wpi_softc *sc)
6047370bffcSMatthew Dillon {
6057370bffcSMatthew Dillon #ifdef WPI_DEBUG
6067370bffcSMatthew Dillon struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
6077370bffcSMatthew Dillon struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
6084db7dd1bSJoe Talbott
6097370bffcSMatthew Dillon SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
6107370bffcSMatthew Dillon "debug", CTLFLAG_RW, &sc->sc_debug, sc->sc_debug,
6117370bffcSMatthew Dillon "control debugging printfs");
6127370bffcSMatthew Dillon #endif
6134db7dd1bSJoe Talbott }
6144db7dd1bSJoe Talbott
6157370bffcSMatthew Dillon static void
wpi_init_beacon(struct wpi_vap * wvp)6167370bffcSMatthew Dillon wpi_init_beacon(struct wpi_vap *wvp)
6177370bffcSMatthew Dillon {
6187370bffcSMatthew Dillon struct wpi_buf *bcn = &wvp->wv_bcbuf;
6197370bffcSMatthew Dillon struct wpi_cmd_beacon *cmd = (struct wpi_cmd_beacon *)&bcn->data;
6204db7dd1bSJoe Talbott
6217370bffcSMatthew Dillon cmd->id = WPI_ID_BROADCAST;
6227370bffcSMatthew Dillon cmd->ofdm_mask = 0xff;
6237370bffcSMatthew Dillon cmd->cck_mask = 0x0f;
6247370bffcSMatthew Dillon cmd->lifetime = htole32(WPI_LIFETIME_INFINITE);
6254db7dd1bSJoe Talbott
6267370bffcSMatthew Dillon /*
6277370bffcSMatthew Dillon * XXX WPI_TX_AUTO_SEQ seems to be ignored - workaround this issue
6287370bffcSMatthew Dillon * XXX by using WPI_TX_NEED_ACK instead (with some side effects).
6297370bffcSMatthew Dillon */
6307370bffcSMatthew Dillon cmd->flags = htole32(WPI_TX_NEED_ACK | WPI_TX_INSERT_TSTAMP);
6314db7dd1bSJoe Talbott
6327370bffcSMatthew Dillon bcn->code = WPI_CMD_SET_BEACON;
6337370bffcSMatthew Dillon bcn->ac = WPI_CMD_QUEUE_NUM;
6347370bffcSMatthew Dillon bcn->size = sizeof(struct wpi_cmd_beacon);
6354db7dd1bSJoe Talbott }
6364db7dd1bSJoe Talbott
6374db7dd1bSJoe Talbott static struct ieee80211vap *
wpi_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])638d0c50e91SJohannes Hofmann wpi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
63918ef6e46SSascha Wildner enum ieee80211_opmode opmode, int flags,
6404db7dd1bSJoe Talbott const uint8_t bssid[IEEE80211_ADDR_LEN],
6414db7dd1bSJoe Talbott const uint8_t mac[IEEE80211_ADDR_LEN])
6424db7dd1bSJoe Talbott {
6434db7dd1bSJoe Talbott struct wpi_vap *wvp;
6444db7dd1bSJoe Talbott struct ieee80211vap *vap;
6454db7dd1bSJoe Talbott
6464db7dd1bSJoe Talbott if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
6474db7dd1bSJoe Talbott return NULL;
6487370bffcSMatthew Dillon
6497370bffcSMatthew Dillon wvp = kmalloc(sizeof(struct wpi_vap), M_80211_VAP, M_WAITOK | M_ZERO);
6507370bffcSMatthew Dillon vap = &wvp->wv_vap;
6517370bffcSMatthew Dillon ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
6527370bffcSMatthew Dillon
6537370bffcSMatthew Dillon if (opmode == IEEE80211_M_IBSS || opmode == IEEE80211_M_HOSTAP) {
6547370bffcSMatthew Dillon WPI_VAP_LOCK_INIT(wvp);
6557370bffcSMatthew Dillon wpi_init_beacon(wvp);
6567370bffcSMatthew Dillon }
6577370bffcSMatthew Dillon
6587370bffcSMatthew Dillon /* Override with driver methods. */
6597370bffcSMatthew Dillon vap->iv_key_set = wpi_key_set;
6607370bffcSMatthew Dillon vap->iv_key_delete = wpi_key_delete;
6617370bffcSMatthew Dillon if (opmode == IEEE80211_M_IBSS) {
6627370bffcSMatthew Dillon wvp->wv_recv_mgmt = vap->iv_recv_mgmt;
6637370bffcSMatthew Dillon vap->iv_recv_mgmt = wpi_ibss_recv_mgmt;
6647370bffcSMatthew Dillon }
6657370bffcSMatthew Dillon wvp->wv_newstate = vap->iv_newstate;
6664db7dd1bSJoe Talbott vap->iv_newstate = wpi_newstate;
6677370bffcSMatthew Dillon vap->iv_update_beacon = wpi_update_beacon;
6687370bffcSMatthew Dillon vap->iv_max_aid = WPI_ID_IBSS_MAX - WPI_ID_IBSS_MIN + 1;
6694db7dd1bSJoe Talbott
6704db7dd1bSJoe Talbott ieee80211_ratectl_init(vap);
6717370bffcSMatthew Dillon /* Complete setup. */
6727370bffcSMatthew Dillon ieee80211_vap_attach(vap, ieee80211_media_change,
6737370bffcSMatthew Dillon ieee80211_media_status, mac);
6744db7dd1bSJoe Talbott ic->ic_opmode = opmode;
6754db7dd1bSJoe Talbott return vap;
6764db7dd1bSJoe Talbott }
6774db7dd1bSJoe Talbott
6784db7dd1bSJoe Talbott static void
wpi_vap_delete(struct ieee80211vap * vap)6794db7dd1bSJoe Talbott wpi_vap_delete(struct ieee80211vap *vap)
6804db7dd1bSJoe Talbott {
6814db7dd1bSJoe Talbott struct wpi_vap *wvp = WPI_VAP(vap);
6827370bffcSMatthew Dillon struct wpi_buf *bcn = &wvp->wv_bcbuf;
6837370bffcSMatthew Dillon enum ieee80211_opmode opmode = vap->iv_opmode;
6844db7dd1bSJoe Talbott
6854db7dd1bSJoe Talbott ieee80211_ratectl_deinit(vap);
6864db7dd1bSJoe Talbott ieee80211_vap_detach(vap);
6877370bffcSMatthew Dillon
6887370bffcSMatthew Dillon if (opmode == IEEE80211_M_IBSS || opmode == IEEE80211_M_HOSTAP) {
6897370bffcSMatthew Dillon if (bcn->m != NULL)
6907370bffcSMatthew Dillon m_freem(bcn->m);
6917370bffcSMatthew Dillon
6927370bffcSMatthew Dillon WPI_VAP_LOCK_DESTROY(wvp);
6937370bffcSMatthew Dillon }
6947370bffcSMatthew Dillon
6954db7dd1bSJoe Talbott kfree(wvp, M_80211_VAP);
6964db7dd1bSJoe Talbott }
6974db7dd1bSJoe Talbott
6987370bffcSMatthew Dillon static int
wpi_detach(device_t dev)6997370bffcSMatthew Dillon wpi_detach(device_t dev)
7007370bffcSMatthew Dillon {
7017370bffcSMatthew Dillon struct wpi_softc *sc = device_get_softc(dev);
7027370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
7037370bffcSMatthew Dillon uint8_t qid;
7047370bffcSMatthew Dillon
7057370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
7067370bffcSMatthew Dillon
7077370bffcSMatthew Dillon if (ic->ic_vap_create == wpi_vap_create) {
7087370bffcSMatthew Dillon ieee80211_draintask(ic, &sc->sc_radioon_task);
7097370bffcSMatthew Dillon ieee80211_draintask(ic, &sc->sc_radiooff_task);
7107370bffcSMatthew Dillon
7117370bffcSMatthew Dillon wpi_stop(sc);
7127370bffcSMatthew Dillon
7137370bffcSMatthew Dillon callout_drain(&sc->watchdog_rfkill);
7147370bffcSMatthew Dillon callout_drain(&sc->tx_timeout);
7157370bffcSMatthew Dillon callout_drain(&sc->scan_timeout);
7167370bffcSMatthew Dillon callout_drain(&sc->calib_to);
7177370bffcSMatthew Dillon ieee80211_ifdetach(ic);
7187370bffcSMatthew Dillon }
7197370bffcSMatthew Dillon
7207370bffcSMatthew Dillon /* Uninstall interrupt handler. */
7217370bffcSMatthew Dillon if (sc->irq != NULL) {
7227370bffcSMatthew Dillon bus_teardown_intr(dev, sc->irq, sc->sc_ih);
7237370bffcSMatthew Dillon bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq),
7247370bffcSMatthew Dillon sc->irq);
7257370bffcSMatthew Dillon pci_release_msi(dev);
7267370bffcSMatthew Dillon }
7277370bffcSMatthew Dillon
7287370bffcSMatthew Dillon if (sc->txq[0].data_dmat) {
7297370bffcSMatthew Dillon /* Free DMA resources. */
7307370bffcSMatthew Dillon for (qid = 0; qid < WPI_DRV_NTXQUEUES; qid++)
7317370bffcSMatthew Dillon wpi_free_tx_ring(sc, &sc->txq[qid]);
7327370bffcSMatthew Dillon
7337370bffcSMatthew Dillon wpi_free_rx_ring(sc);
7347370bffcSMatthew Dillon wpi_free_shared(sc);
7357370bffcSMatthew Dillon }
7367370bffcSMatthew Dillon
7377370bffcSMatthew Dillon if (sc->fw_dma.tag)
7387370bffcSMatthew Dillon wpi_free_fwmem(sc);
7397370bffcSMatthew Dillon
7407370bffcSMatthew Dillon if (sc->mem != NULL)
7417370bffcSMatthew Dillon bus_release_resource(dev, SYS_RES_MEMORY,
7427370bffcSMatthew Dillon rman_get_rid(sc->mem), sc->mem);
7437370bffcSMatthew Dillon
7447370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
7457370bffcSMatthew Dillon WPI_TXQ_STATE_LOCK_DESTROY(sc);
7467370bffcSMatthew Dillon WPI_TXQ_LOCK_DESTROY(sc);
7477370bffcSMatthew Dillon WPI_NT_LOCK_DESTROY(sc);
7487370bffcSMatthew Dillon WPI_RXON_LOCK_DESTROY(sc);
7497370bffcSMatthew Dillon WPI_TX_LOCK_DESTROY(sc);
7507370bffcSMatthew Dillon WPI_LOCK_DESTROY(sc);
7517370bffcSMatthew Dillon return 0;
7527370bffcSMatthew Dillon }
7537370bffcSMatthew Dillon
7547370bffcSMatthew Dillon static int
wpi_shutdown(device_t dev)7557370bffcSMatthew Dillon wpi_shutdown(device_t dev)
7567370bffcSMatthew Dillon {
7577370bffcSMatthew Dillon struct wpi_softc *sc = device_get_softc(dev);
7587370bffcSMatthew Dillon
7597370bffcSMatthew Dillon wpi_stop(sc);
7607370bffcSMatthew Dillon return 0;
7617370bffcSMatthew Dillon }
7627370bffcSMatthew Dillon
7637370bffcSMatthew Dillon static int
wpi_suspend(device_t dev)7647370bffcSMatthew Dillon wpi_suspend(device_t dev)
7657370bffcSMatthew Dillon {
7667370bffcSMatthew Dillon struct wpi_softc *sc = device_get_softc(dev);
7677370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
7687370bffcSMatthew Dillon
7697370bffcSMatthew Dillon ieee80211_suspend_all(ic);
7707370bffcSMatthew Dillon return 0;
7717370bffcSMatthew Dillon }
7727370bffcSMatthew Dillon
7737370bffcSMatthew Dillon static int
wpi_resume(device_t dev)7747370bffcSMatthew Dillon wpi_resume(device_t dev)
7757370bffcSMatthew Dillon {
7767370bffcSMatthew Dillon struct wpi_softc *sc = device_get_softc(dev);
7777370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
7787370bffcSMatthew Dillon
7797370bffcSMatthew Dillon /* Clear device-specific "PCI retry timeout" register (41h). */
7807370bffcSMatthew Dillon pci_write_config(dev, 0x41, 0, 1);
7817370bffcSMatthew Dillon
7827370bffcSMatthew Dillon ieee80211_resume_all(ic);
7837370bffcSMatthew Dillon return 0;
7847370bffcSMatthew Dillon }
7857370bffcSMatthew Dillon
7867370bffcSMatthew Dillon /*
7877370bffcSMatthew Dillon * Grab exclusive access to NIC memory.
7887370bffcSMatthew Dillon */
7897370bffcSMatthew Dillon static int
wpi_nic_lock(struct wpi_softc * sc)7907370bffcSMatthew Dillon wpi_nic_lock(struct wpi_softc *sc)
7917370bffcSMatthew Dillon {
7927370bffcSMatthew Dillon int ntries;
7937370bffcSMatthew Dillon
7947370bffcSMatthew Dillon /* Request exclusive access to NIC. */
7957370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
7967370bffcSMatthew Dillon
7977370bffcSMatthew Dillon /* Spin until we actually get the lock. */
7987370bffcSMatthew Dillon for (ntries = 0; ntries < 1000; ntries++) {
7997370bffcSMatthew Dillon if ((WPI_READ(sc, WPI_GP_CNTRL) &
8007370bffcSMatthew Dillon (WPI_GP_CNTRL_MAC_ACCESS_ENA | WPI_GP_CNTRL_SLEEP)) ==
8017370bffcSMatthew Dillon WPI_GP_CNTRL_MAC_ACCESS_ENA)
8027370bffcSMatthew Dillon return 0;
8037370bffcSMatthew Dillon DELAY(10);
8047370bffcSMatthew Dillon }
8057370bffcSMatthew Dillon
8067370bffcSMatthew Dillon device_printf(sc->sc_dev, "could not lock memory\n");
8077370bffcSMatthew Dillon
8087370bffcSMatthew Dillon return ETIMEDOUT;
8097370bffcSMatthew Dillon }
8107370bffcSMatthew Dillon
8117370bffcSMatthew Dillon /*
8127370bffcSMatthew Dillon * Release lock on NIC memory.
8137370bffcSMatthew Dillon */
8147370bffcSMatthew Dillon static __inline void
wpi_nic_unlock(struct wpi_softc * sc)8157370bffcSMatthew Dillon wpi_nic_unlock(struct wpi_softc *sc)
8167370bffcSMatthew Dillon {
8177370bffcSMatthew Dillon WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
8187370bffcSMatthew Dillon }
8197370bffcSMatthew Dillon
8207370bffcSMatthew Dillon static __inline uint32_t
wpi_prph_read(struct wpi_softc * sc,uint32_t addr)8217370bffcSMatthew Dillon wpi_prph_read(struct wpi_softc *sc, uint32_t addr)
8227370bffcSMatthew Dillon {
8237370bffcSMatthew Dillon WPI_WRITE(sc, WPI_PRPH_RADDR, WPI_PRPH_DWORD | addr);
8247370bffcSMatthew Dillon WPI_BARRIER_READ_WRITE(sc);
8257370bffcSMatthew Dillon return WPI_READ(sc, WPI_PRPH_RDATA);
8267370bffcSMatthew Dillon }
8277370bffcSMatthew Dillon
8287370bffcSMatthew Dillon static __inline void
wpi_prph_write(struct wpi_softc * sc,uint32_t addr,uint32_t data)8297370bffcSMatthew Dillon wpi_prph_write(struct wpi_softc *sc, uint32_t addr, uint32_t data)
8307370bffcSMatthew Dillon {
8317370bffcSMatthew Dillon WPI_WRITE(sc, WPI_PRPH_WADDR, WPI_PRPH_DWORD | addr);
8327370bffcSMatthew Dillon WPI_BARRIER_WRITE(sc);
8337370bffcSMatthew Dillon WPI_WRITE(sc, WPI_PRPH_WDATA, data);
8347370bffcSMatthew Dillon }
8357370bffcSMatthew Dillon
8367370bffcSMatthew Dillon static __inline void
wpi_prph_setbits(struct wpi_softc * sc,uint32_t addr,uint32_t mask)8377370bffcSMatthew Dillon wpi_prph_setbits(struct wpi_softc *sc, uint32_t addr, uint32_t mask)
8387370bffcSMatthew Dillon {
8397370bffcSMatthew Dillon wpi_prph_write(sc, addr, wpi_prph_read(sc, addr) | mask);
8407370bffcSMatthew Dillon }
8417370bffcSMatthew Dillon
8427370bffcSMatthew Dillon static __inline void
wpi_prph_clrbits(struct wpi_softc * sc,uint32_t addr,uint32_t mask)8437370bffcSMatthew Dillon wpi_prph_clrbits(struct wpi_softc *sc, uint32_t addr, uint32_t mask)
8447370bffcSMatthew Dillon {
8457370bffcSMatthew Dillon wpi_prph_write(sc, addr, wpi_prph_read(sc, addr) & ~mask);
8467370bffcSMatthew Dillon }
8477370bffcSMatthew Dillon
8487370bffcSMatthew Dillon static __inline void
wpi_prph_write_region_4(struct wpi_softc * sc,uint32_t addr,const uint32_t * data,uint32_t count)8497370bffcSMatthew Dillon wpi_prph_write_region_4(struct wpi_softc *sc, uint32_t addr,
8507370bffcSMatthew Dillon const uint32_t *data, uint32_t count)
8517370bffcSMatthew Dillon {
8527370bffcSMatthew Dillon for (; count != 0; count--, data++, addr += 4)
8537370bffcSMatthew Dillon wpi_prph_write(sc, addr, *data);
8547370bffcSMatthew Dillon }
8557370bffcSMatthew Dillon
8567370bffcSMatthew Dillon static __inline uint32_t
wpi_mem_read(struct wpi_softc * sc,uint32_t addr)8577370bffcSMatthew Dillon wpi_mem_read(struct wpi_softc *sc, uint32_t addr)
8587370bffcSMatthew Dillon {
8597370bffcSMatthew Dillon WPI_WRITE(sc, WPI_MEM_RADDR, addr);
8607370bffcSMatthew Dillon WPI_BARRIER_READ_WRITE(sc);
8617370bffcSMatthew Dillon return WPI_READ(sc, WPI_MEM_RDATA);
8627370bffcSMatthew Dillon }
8637370bffcSMatthew Dillon
8647370bffcSMatthew Dillon static __inline void
wpi_mem_read_region_4(struct wpi_softc * sc,uint32_t addr,uint32_t * data,int count)8657370bffcSMatthew Dillon wpi_mem_read_region_4(struct wpi_softc *sc, uint32_t addr, uint32_t *data,
8667370bffcSMatthew Dillon int count)
8677370bffcSMatthew Dillon {
8687370bffcSMatthew Dillon for (; count > 0; count--, addr += 4)
8697370bffcSMatthew Dillon *data++ = wpi_mem_read(sc, addr);
8707370bffcSMatthew Dillon }
8717370bffcSMatthew Dillon
8727370bffcSMatthew Dillon static int
wpi_read_prom_data(struct wpi_softc * sc,uint32_t addr,void * data,int count)8737370bffcSMatthew Dillon wpi_read_prom_data(struct wpi_softc *sc, uint32_t addr, void *data, int count)
8747370bffcSMatthew Dillon {
8757370bffcSMatthew Dillon uint8_t *out = data;
8767370bffcSMatthew Dillon uint32_t val;
8777370bffcSMatthew Dillon int error, ntries;
8787370bffcSMatthew Dillon
8797370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
8807370bffcSMatthew Dillon
8817370bffcSMatthew Dillon if ((error = wpi_nic_lock(sc)) != 0)
8827370bffcSMatthew Dillon return error;
8837370bffcSMatthew Dillon
8847370bffcSMatthew Dillon for (; count > 0; count -= 2, addr++) {
8857370bffcSMatthew Dillon WPI_WRITE(sc, WPI_EEPROM, addr << 2);
8867370bffcSMatthew Dillon for (ntries = 0; ntries < 10; ntries++) {
8877370bffcSMatthew Dillon val = WPI_READ(sc, WPI_EEPROM);
8887370bffcSMatthew Dillon if (val & WPI_EEPROM_READ_VALID)
8897370bffcSMatthew Dillon break;
8907370bffcSMatthew Dillon DELAY(5);
8917370bffcSMatthew Dillon }
8927370bffcSMatthew Dillon if (ntries == 10) {
8937370bffcSMatthew Dillon device_printf(sc->sc_dev,
8947370bffcSMatthew Dillon "timeout reading ROM at 0x%x\n", addr);
8957370bffcSMatthew Dillon return ETIMEDOUT;
8967370bffcSMatthew Dillon }
8977370bffcSMatthew Dillon *out++= val >> 16;
8987370bffcSMatthew Dillon if (count > 1)
8997370bffcSMatthew Dillon *out ++= val >> 24;
9007370bffcSMatthew Dillon }
9017370bffcSMatthew Dillon
9027370bffcSMatthew Dillon wpi_nic_unlock(sc);
9037370bffcSMatthew Dillon
9047370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
9057370bffcSMatthew Dillon
9067370bffcSMatthew Dillon return 0;
9077370bffcSMatthew Dillon }
9087370bffcSMatthew Dillon
9094db7dd1bSJoe Talbott static void
wpi_dma_map_addr(void * arg,bus_dma_segment_t * segs,int nsegs,int error)9104db7dd1bSJoe Talbott wpi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
9114db7dd1bSJoe Talbott {
9124db7dd1bSJoe Talbott if (error != 0)
9134db7dd1bSJoe Talbott return;
9144db7dd1bSJoe Talbott KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
9154db7dd1bSJoe Talbott *(bus_addr_t *)arg = segs[0].ds_addr;
9164db7dd1bSJoe Talbott }
9174db7dd1bSJoe Talbott
9184db7dd1bSJoe Talbott /*
9194db7dd1bSJoe Talbott * Allocates a contiguous block of dma memory of the requested size and
9207370bffcSMatthew Dillon * alignment.
9214db7dd1bSJoe Talbott */
9224db7dd1bSJoe Talbott static int
wpi_dma_contig_alloc(struct wpi_softc * sc,struct wpi_dma_info * dma,void ** kvap,bus_size_t size,bus_size_t alignment)9234db7dd1bSJoe Talbott wpi_dma_contig_alloc(struct wpi_softc *sc, struct wpi_dma_info *dma,
9247370bffcSMatthew Dillon void **kvap, bus_size_t size, bus_size_t alignment)
9254db7dd1bSJoe Talbott {
9264db7dd1bSJoe Talbott int error;
9274db7dd1bSJoe Talbott
9284db7dd1bSJoe Talbott dma->tag = NULL;
9297370bffcSMatthew Dillon dma->size = size;
9304db7dd1bSJoe Talbott
931d0c50e91SJohannes Hofmann #if defined(__DragonFly__)
9327370bffcSMatthew Dillon error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment,
933*030b0c8cSMichael Neumann 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, size,
9347370bffcSMatthew Dillon 1, size, 0, &dma->tag);
935d0c50e91SJohannes Hofmann #else
9367370bffcSMatthew Dillon error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment,
9377370bffcSMatthew Dillon 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
9387370bffcSMatthew Dillon 1, size, 0, NULL, NULL, &dma->tag);
939d0c50e91SJohannes Hofmann #endif
9407370bffcSMatthew Dillon if (error != 0)
9414db7dd1bSJoe Talbott goto fail;
9427370bffcSMatthew Dillon
9437370bffcSMatthew Dillon error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
9447370bffcSMatthew Dillon BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map);
9457370bffcSMatthew Dillon if (error != 0)
9464db7dd1bSJoe Talbott goto fail;
9474db7dd1bSJoe Talbott
9487370bffcSMatthew Dillon error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size,
9497370bffcSMatthew Dillon wpi_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT);
9507370bffcSMatthew Dillon if (error != 0)
9514db7dd1bSJoe Talbott goto fail;
9524db7dd1bSJoe Talbott
9537370bffcSMatthew Dillon bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
9544db7dd1bSJoe Talbott
9554db7dd1bSJoe Talbott if (kvap != NULL)
9564db7dd1bSJoe Talbott *kvap = dma->vaddr;
9574db7dd1bSJoe Talbott
9584db7dd1bSJoe Talbott return 0;
9594db7dd1bSJoe Talbott
9607370bffcSMatthew Dillon fail: wpi_dma_contig_free(dma);
9614db7dd1bSJoe Talbott return error;
9624db7dd1bSJoe Talbott }
9634db7dd1bSJoe Talbott
9644db7dd1bSJoe Talbott static void
wpi_dma_contig_free(struct wpi_dma_info * dma)9654db7dd1bSJoe Talbott wpi_dma_contig_free(struct wpi_dma_info *dma)
9664db7dd1bSJoe Talbott {
9677370bffcSMatthew Dillon if (dma->vaddr != NULL) {
9684db7dd1bSJoe Talbott bus_dmamap_sync(dma->tag, dma->map,
9694db7dd1bSJoe Talbott BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
9704db7dd1bSJoe Talbott bus_dmamap_unload(dma->tag, dma->map);
9717370bffcSMatthew Dillon bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
9727370bffcSMatthew Dillon dma->vaddr = NULL;
9734db7dd1bSJoe Talbott }
9747370bffcSMatthew Dillon if (dma->tag != NULL) {
9754db7dd1bSJoe Talbott bus_dma_tag_destroy(dma->tag);
9767370bffcSMatthew Dillon dma->tag = NULL;
9774db7dd1bSJoe Talbott }
9784db7dd1bSJoe Talbott }
9794db7dd1bSJoe Talbott
9804db7dd1bSJoe Talbott /*
9814db7dd1bSJoe Talbott * Allocate a shared page between host and NIC.
9824db7dd1bSJoe Talbott */
9834db7dd1bSJoe Talbott static int
wpi_alloc_shared(struct wpi_softc * sc)9844db7dd1bSJoe Talbott wpi_alloc_shared(struct wpi_softc *sc)
9854db7dd1bSJoe Talbott {
9867370bffcSMatthew Dillon /* Shared buffer must be aligned on a 4KB boundary. */
9877370bffcSMatthew Dillon return wpi_dma_contig_alloc(sc, &sc->shared_dma,
9887370bffcSMatthew Dillon (void **)&sc->shared, sizeof (struct wpi_shared), 4096);
9894db7dd1bSJoe Talbott }
9904db7dd1bSJoe Talbott
9914db7dd1bSJoe Talbott static void
wpi_free_shared(struct wpi_softc * sc)9924db7dd1bSJoe Talbott wpi_free_shared(struct wpi_softc *sc)
9934db7dd1bSJoe Talbott {
9944db7dd1bSJoe Talbott wpi_dma_contig_free(&sc->shared_dma);
9954db7dd1bSJoe Talbott }
9964db7dd1bSJoe Talbott
9977370bffcSMatthew Dillon /*
9987370bffcSMatthew Dillon * Allocate DMA-safe memory for firmware transfer.
9997370bffcSMatthew Dillon */
10004db7dd1bSJoe Talbott static int
wpi_alloc_fwmem(struct wpi_softc * sc)10017370bffcSMatthew Dillon wpi_alloc_fwmem(struct wpi_softc *sc)
10024db7dd1bSJoe Talbott {
10037370bffcSMatthew Dillon /* Must be aligned on a 16-byte boundary. */
10047370bffcSMatthew Dillon return wpi_dma_contig_alloc(sc, &sc->fw_dma, NULL,
10057370bffcSMatthew Dillon WPI_FW_TEXT_MAXSZ + WPI_FW_DATA_MAXSZ, 16);
10067370bffcSMatthew Dillon }
10074db7dd1bSJoe Talbott
10087370bffcSMatthew Dillon static void
wpi_free_fwmem(struct wpi_softc * sc)10097370bffcSMatthew Dillon wpi_free_fwmem(struct wpi_softc *sc)
10107370bffcSMatthew Dillon {
10117370bffcSMatthew Dillon wpi_dma_contig_free(&sc->fw_dma);
10127370bffcSMatthew Dillon }
10137370bffcSMatthew Dillon
10147370bffcSMatthew Dillon static int
wpi_alloc_rx_ring(struct wpi_softc * sc)10157370bffcSMatthew Dillon wpi_alloc_rx_ring(struct wpi_softc *sc)
10167370bffcSMatthew Dillon {
10177370bffcSMatthew Dillon struct wpi_rx_ring *ring = &sc->rxq;
10187370bffcSMatthew Dillon bus_size_t size;
10194db7dd1bSJoe Talbott int i, error;
10204db7dd1bSJoe Talbott
10214db7dd1bSJoe Talbott ring->cur = 0;
10227370bffcSMatthew Dillon ring->update = 0;
10234db7dd1bSJoe Talbott
10247370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
10257370bffcSMatthew Dillon
10267370bffcSMatthew Dillon /* Allocate RX descriptors (16KB aligned.) */
10277370bffcSMatthew Dillon size = WPI_RX_RING_COUNT * sizeof (uint32_t);
10284db7dd1bSJoe Talbott error = wpi_dma_contig_alloc(sc, &ring->desc_dma,
10297370bffcSMatthew Dillon (void **)&ring->desc, size, WPI_RING_DMA_ALIGN);
10304db7dd1bSJoe Talbott if (error != 0) {
10314db7dd1bSJoe Talbott device_printf(sc->sc_dev,
10327370bffcSMatthew Dillon "%s: could not allocate RX ring DMA memory, error %d\n",
10334db7dd1bSJoe Talbott __func__, error);
10344db7dd1bSJoe Talbott goto fail;
10354db7dd1bSJoe Talbott }
10364db7dd1bSJoe Talbott
10377370bffcSMatthew Dillon /* Create RX buffer DMA tag. */
1038d0c50e91SJohannes Hofmann #if defined(__DragonFly__)
1039d0c50e91SJohannes Hofmann error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1040*030b0c8cSMichael Neumann BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
10417370bffcSMatthew Dillon MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, &ring->data_dmat);
1042d0c50e91SJohannes Hofmann #else
1043d0c50e91SJohannes Hofmann error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
10447370bffcSMatthew Dillon BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
10457370bffcSMatthew Dillon MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, &ring->data_dmat);
1046d0c50e91SJohannes Hofmann #endif
10474db7dd1bSJoe Talbott if (error != 0) {
10484db7dd1bSJoe Talbott device_printf(sc->sc_dev,
10497370bffcSMatthew Dillon "%s: could not create RX buf DMA tag, error %d\n",
10504db7dd1bSJoe Talbott __func__, error);
10514db7dd1bSJoe Talbott goto fail;
10524db7dd1bSJoe Talbott }
10534db7dd1bSJoe Talbott
10544db7dd1bSJoe Talbott /*
10557370bffcSMatthew Dillon * Allocate and map RX buffers.
10564db7dd1bSJoe Talbott */
10574db7dd1bSJoe Talbott for (i = 0; i < WPI_RX_RING_COUNT; i++) {
10584db7dd1bSJoe Talbott struct wpi_rx_data *data = &ring->data[i];
10594db7dd1bSJoe Talbott bus_addr_t paddr;
10604db7dd1bSJoe Talbott
10614db7dd1bSJoe Talbott error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
10624db7dd1bSJoe Talbott if (error != 0) {
10634db7dd1bSJoe Talbott device_printf(sc->sc_dev,
10647370bffcSMatthew Dillon "%s: could not create RX buf DMA map, error %d\n",
10654db7dd1bSJoe Talbott __func__, error);
10664db7dd1bSJoe Talbott goto fail;
10674db7dd1bSJoe Talbott }
10687370bffcSMatthew Dillon
10697370bffcSMatthew Dillon data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
10707370bffcSMatthew Dillon if (data->m == NULL) {
10714db7dd1bSJoe Talbott device_printf(sc->sc_dev,
10727370bffcSMatthew Dillon "%s: could not allocate RX mbuf\n", __func__);
10737370bffcSMatthew Dillon error = ENOBUFS;
10744db7dd1bSJoe Talbott goto fail;
10754db7dd1bSJoe Talbott }
10767370bffcSMatthew Dillon
10774db7dd1bSJoe Talbott error = bus_dmamap_load(ring->data_dmat, data->map,
10787370bffcSMatthew Dillon mtod(data->m, void *), MJUMPAGESIZE, wpi_dma_map_addr,
10797370bffcSMatthew Dillon &paddr, BUS_DMA_NOWAIT);
10804db7dd1bSJoe Talbott if (error != 0 && error != EFBIG) {
10814db7dd1bSJoe Talbott device_printf(sc->sc_dev,
10827370bffcSMatthew Dillon "%s: can't map mbuf (error %d)\n", __func__,
10837370bffcSMatthew Dillon error);
10844db7dd1bSJoe Talbott goto fail;
10854db7dd1bSJoe Talbott }
10864db7dd1bSJoe Talbott
10877370bffcSMatthew Dillon /* Set physical address of RX buffer. */
10884db7dd1bSJoe Talbott ring->desc[i] = htole32(paddr);
10894db7dd1bSJoe Talbott }
10907370bffcSMatthew Dillon
10914db7dd1bSJoe Talbott bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
10924db7dd1bSJoe Talbott BUS_DMASYNC_PREWRITE);
10937370bffcSMatthew Dillon
10947370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
10957370bffcSMatthew Dillon
10964db7dd1bSJoe Talbott return 0;
10977370bffcSMatthew Dillon
10987370bffcSMatthew Dillon fail: wpi_free_rx_ring(sc);
10997370bffcSMatthew Dillon
11007370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__);
11017370bffcSMatthew Dillon
11024db7dd1bSJoe Talbott return error;
11034db7dd1bSJoe Talbott }
11044db7dd1bSJoe Talbott
11054db7dd1bSJoe Talbott static void
wpi_update_rx_ring(struct wpi_softc * sc)11067370bffcSMatthew Dillon wpi_update_rx_ring(struct wpi_softc *sc)
11074db7dd1bSJoe Talbott {
11087370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_RX_WPTR, sc->rxq.cur & ~7);
11094db7dd1bSJoe Talbott }
11104db7dd1bSJoe Talbott
11114db7dd1bSJoe Talbott static void
wpi_update_rx_ring_ps(struct wpi_softc * sc)11127370bffcSMatthew Dillon wpi_update_rx_ring_ps(struct wpi_softc *sc)
11134db7dd1bSJoe Talbott {
11147370bffcSMatthew Dillon struct wpi_rx_ring *ring = &sc->rxq;
11157370bffcSMatthew Dillon
11167370bffcSMatthew Dillon if (ring->update != 0) {
11177370bffcSMatthew Dillon /* Wait for INT_WAKEUP event. */
11187370bffcSMatthew Dillon return;
11197370bffcSMatthew Dillon }
11207370bffcSMatthew Dillon
11217370bffcSMatthew Dillon WPI_TXQ_LOCK(sc);
11227370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
11237370bffcSMatthew Dillon if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_SLEEP) {
11247370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s: wakeup request\n",
11257370bffcSMatthew Dillon __func__);
11267370bffcSMatthew Dillon ring->update = 1;
11277370bffcSMatthew Dillon } else {
11287370bffcSMatthew Dillon wpi_update_rx_ring(sc);
11297370bffcSMatthew Dillon WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
11307370bffcSMatthew Dillon }
11317370bffcSMatthew Dillon WPI_TXQ_UNLOCK(sc);
11327370bffcSMatthew Dillon }
11337370bffcSMatthew Dillon
11347370bffcSMatthew Dillon static void
wpi_reset_rx_ring(struct wpi_softc * sc)11357370bffcSMatthew Dillon wpi_reset_rx_ring(struct wpi_softc *sc)
11367370bffcSMatthew Dillon {
11377370bffcSMatthew Dillon struct wpi_rx_ring *ring = &sc->rxq;
11387370bffcSMatthew Dillon int ntries;
11397370bffcSMatthew Dillon
11407370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
11417370bffcSMatthew Dillon
11427370bffcSMatthew Dillon if (wpi_nic_lock(sc) == 0) {
11437370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_RX_CONFIG, 0);
11447370bffcSMatthew Dillon for (ntries = 0; ntries < 1000; ntries++) {
11457370bffcSMatthew Dillon if (WPI_READ(sc, WPI_FH_RX_STATUS) &
11467370bffcSMatthew Dillon WPI_FH_RX_STATUS_IDLE)
11477370bffcSMatthew Dillon break;
11487370bffcSMatthew Dillon DELAY(10);
11497370bffcSMatthew Dillon }
11507370bffcSMatthew Dillon wpi_nic_unlock(sc);
11517370bffcSMatthew Dillon }
11527370bffcSMatthew Dillon
11537370bffcSMatthew Dillon ring->cur = 0;
11547370bffcSMatthew Dillon ring->update = 0;
11557370bffcSMatthew Dillon }
11567370bffcSMatthew Dillon
11577370bffcSMatthew Dillon static void
wpi_free_rx_ring(struct wpi_softc * sc)11587370bffcSMatthew Dillon wpi_free_rx_ring(struct wpi_softc *sc)
11597370bffcSMatthew Dillon {
11607370bffcSMatthew Dillon struct wpi_rx_ring *ring = &sc->rxq;
11614db7dd1bSJoe Talbott int i;
11624db7dd1bSJoe Talbott
11637370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
11647370bffcSMatthew Dillon
11654db7dd1bSJoe Talbott wpi_dma_contig_free(&ring->desc_dma);
11664db7dd1bSJoe Talbott
1167d0c50e91SJohannes Hofmann for (i = 0; i < WPI_RX_RING_COUNT; i++) {
1168d0c50e91SJohannes Hofmann struct wpi_rx_data *data = &ring->data[i];
1169d0c50e91SJohannes Hofmann
1170d0c50e91SJohannes Hofmann if (data->m != NULL) {
1171d0c50e91SJohannes Hofmann bus_dmamap_sync(ring->data_dmat, data->map,
1172d0c50e91SJohannes Hofmann BUS_DMASYNC_POSTREAD);
1173d0c50e91SJohannes Hofmann bus_dmamap_unload(ring->data_dmat, data->map);
1174d0c50e91SJohannes Hofmann m_freem(data->m);
11757370bffcSMatthew Dillon data->m = NULL;
1176d0c50e91SJohannes Hofmann }
1177d0c50e91SJohannes Hofmann if (data->map != NULL)
1178d0c50e91SJohannes Hofmann bus_dmamap_destroy(ring->data_dmat, data->map);
1179d0c50e91SJohannes Hofmann }
11807370bffcSMatthew Dillon if (ring->data_dmat != NULL) {
11817370bffcSMatthew Dillon bus_dma_tag_destroy(ring->data_dmat);
11827370bffcSMatthew Dillon ring->data_dmat = NULL;
11837370bffcSMatthew Dillon }
11844db7dd1bSJoe Talbott }
11854db7dd1bSJoe Talbott
11864db7dd1bSJoe Talbott static int
wpi_alloc_tx_ring(struct wpi_softc * sc,struct wpi_tx_ring * ring,uint8_t qid)11877370bffcSMatthew Dillon wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, uint8_t qid)
11884db7dd1bSJoe Talbott {
11897370bffcSMatthew Dillon bus_addr_t paddr;
11907370bffcSMatthew Dillon bus_size_t size;
11914db7dd1bSJoe Talbott int i, error;
11924db7dd1bSJoe Talbott
11934db7dd1bSJoe Talbott ring->qid = qid;
11944db7dd1bSJoe Talbott ring->queued = 0;
11954db7dd1bSJoe Talbott ring->cur = 0;
11967370bffcSMatthew Dillon ring->pending = 0;
11977370bffcSMatthew Dillon ring->update = 0;
11984db7dd1bSJoe Talbott
11997370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
12004db7dd1bSJoe Talbott
12017370bffcSMatthew Dillon /* Allocate TX descriptors (16KB aligned.) */
12027370bffcSMatthew Dillon size = WPI_TX_RING_COUNT * sizeof (struct wpi_tx_desc);
12037370bffcSMatthew Dillon error = wpi_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc,
12047370bffcSMatthew Dillon size, WPI_RING_DMA_ALIGN);
12054db7dd1bSJoe Talbott if (error != 0) {
12067370bffcSMatthew Dillon device_printf(sc->sc_dev,
12077370bffcSMatthew Dillon "%s: could not allocate TX ring DMA memory, error %d\n",
12087370bffcSMatthew Dillon __func__, error);
12094db7dd1bSJoe Talbott goto fail;
12104db7dd1bSJoe Talbott }
12114db7dd1bSJoe Talbott
12127370bffcSMatthew Dillon /* Update shared area with ring physical address. */
12134db7dd1bSJoe Talbott sc->shared->txbase[qid] = htole32(ring->desc_dma.paddr);
12147370bffcSMatthew Dillon bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map,
12157370bffcSMatthew Dillon BUS_DMASYNC_PREWRITE);
12164db7dd1bSJoe Talbott
12177370bffcSMatthew Dillon size = WPI_TX_RING_COUNT * sizeof (struct wpi_tx_cmd);
12184db7dd1bSJoe Talbott error = wpi_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd,
12197370bffcSMatthew Dillon size, 4);
12204db7dd1bSJoe Talbott if (error != 0) {
12214db7dd1bSJoe Talbott device_printf(sc->sc_dev,
12227370bffcSMatthew Dillon "%s: could not allocate TX cmd DMA memory, error %d\n",
12237370bffcSMatthew Dillon __func__, error);
12244db7dd1bSJoe Talbott goto fail;
12254db7dd1bSJoe Talbott }
12264db7dd1bSJoe Talbott
1227d0c50e91SJohannes Hofmann #if defined(__DragonFly__)
1228d0c50e91SJohannes Hofmann error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1229*030b0c8cSMichael Neumann BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, MCLBYTES,
12307370bffcSMatthew Dillon WPI_MAX_SCATTER - 1, MCLBYTES, 0, &ring->data_dmat);
1231d0c50e91SJohannes Hofmann #else
1232d0c50e91SJohannes Hofmann error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1233d0c50e91SJohannes Hofmann BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
12347370bffcSMatthew Dillon WPI_MAX_SCATTER - 1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat);
1235d0c50e91SJohannes Hofmann #endif
12364db7dd1bSJoe Talbott if (error != 0) {
12377370bffcSMatthew Dillon device_printf(sc->sc_dev,
12387370bffcSMatthew Dillon "%s: could not create TX buf DMA tag, error %d\n",
12397370bffcSMatthew Dillon __func__, error);
12404db7dd1bSJoe Talbott goto fail;
12414db7dd1bSJoe Talbott }
12424db7dd1bSJoe Talbott
12437370bffcSMatthew Dillon paddr = ring->cmd_dma.paddr;
12447370bffcSMatthew Dillon for (i = 0; i < WPI_TX_RING_COUNT; i++) {
12457370bffcSMatthew Dillon struct wpi_tx_data *data = &ring->data[i];
12467370bffcSMatthew Dillon
12477370bffcSMatthew Dillon data->cmd_paddr = paddr;
12487370bffcSMatthew Dillon paddr += sizeof (struct wpi_tx_cmd);
12494db7dd1bSJoe Talbott
12504db7dd1bSJoe Talbott error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
12514db7dd1bSJoe Talbott if (error != 0) {
12524db7dd1bSJoe Talbott device_printf(sc->sc_dev,
12537370bffcSMatthew Dillon "%s: could not create TX buf DMA map, error %d\n",
12547370bffcSMatthew Dillon __func__, error);
12554db7dd1bSJoe Talbott goto fail;
12564db7dd1bSJoe Talbott }
12574db7dd1bSJoe Talbott }
12584db7dd1bSJoe Talbott
12597370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
12607370bffcSMatthew Dillon
12614db7dd1bSJoe Talbott return 0;
12624db7dd1bSJoe Talbott
12637370bffcSMatthew Dillon fail: wpi_free_tx_ring(sc, ring);
12647370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__);
12654db7dd1bSJoe Talbott return error;
12664db7dd1bSJoe Talbott }
12674db7dd1bSJoe Talbott
12684db7dd1bSJoe Talbott static void
wpi_update_tx_ring(struct wpi_softc * sc,struct wpi_tx_ring * ring)12697370bffcSMatthew Dillon wpi_update_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring)
12707370bffcSMatthew Dillon {
12717370bffcSMatthew Dillon WPI_WRITE(sc, WPI_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur);
12727370bffcSMatthew Dillon }
12737370bffcSMatthew Dillon
12747370bffcSMatthew Dillon static void
wpi_update_tx_ring_ps(struct wpi_softc * sc,struct wpi_tx_ring * ring)12757370bffcSMatthew Dillon wpi_update_tx_ring_ps(struct wpi_softc *sc, struct wpi_tx_ring *ring)
12767370bffcSMatthew Dillon {
12777370bffcSMatthew Dillon
12787370bffcSMatthew Dillon if (ring->update != 0) {
12797370bffcSMatthew Dillon /* Wait for INT_WAKEUP event. */
12807370bffcSMatthew Dillon return;
12817370bffcSMatthew Dillon }
12827370bffcSMatthew Dillon
12837370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
12847370bffcSMatthew Dillon if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_SLEEP) {
12857370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s (%d): requesting wakeup\n",
12867370bffcSMatthew Dillon __func__, ring->qid);
12877370bffcSMatthew Dillon ring->update = 1;
12887370bffcSMatthew Dillon } else {
12897370bffcSMatthew Dillon wpi_update_tx_ring(sc, ring);
12907370bffcSMatthew Dillon WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
12917370bffcSMatthew Dillon }
12927370bffcSMatthew Dillon }
12937370bffcSMatthew Dillon
12947370bffcSMatthew Dillon static void
wpi_reset_tx_ring(struct wpi_softc * sc,struct wpi_tx_ring * ring)12954db7dd1bSJoe Talbott wpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring)
12964db7dd1bSJoe Talbott {
12974db7dd1bSJoe Talbott int i;
12984db7dd1bSJoe Talbott
12997370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
13004db7dd1bSJoe Talbott
13017370bffcSMatthew Dillon for (i = 0; i < WPI_TX_RING_COUNT; i++) {
13027370bffcSMatthew Dillon struct wpi_tx_data *data = &ring->data[i];
13034db7dd1bSJoe Talbott
13044db7dd1bSJoe Talbott if (data->m != NULL) {
13054db7dd1bSJoe Talbott bus_dmamap_sync(ring->data_dmat, data->map,
13064db7dd1bSJoe Talbott BUS_DMASYNC_POSTWRITE);
13074db7dd1bSJoe Talbott bus_dmamap_unload(ring->data_dmat, data->map);
13084db7dd1bSJoe Talbott m_freem(data->m);
13094db7dd1bSJoe Talbott data->m = NULL;
13104db7dd1bSJoe Talbott }
13117370bffcSMatthew Dillon if (data->ni != NULL) {
13127370bffcSMatthew Dillon ieee80211_free_node(data->ni);
13137370bffcSMatthew Dillon data->ni = NULL;
13144db7dd1bSJoe Talbott }
13157370bffcSMatthew Dillon }
13167370bffcSMatthew Dillon /* Clear TX descriptors. */
13177370bffcSMatthew Dillon memset(ring->desc, 0, ring->desc_dma.size);
13187370bffcSMatthew Dillon bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
13197370bffcSMatthew Dillon BUS_DMASYNC_PREWRITE);
13207370bffcSMatthew Dillon ring->queued = 0;
13217370bffcSMatthew Dillon ring->cur = 0;
13227370bffcSMatthew Dillon ring->pending = 0;
13237370bffcSMatthew Dillon ring->update = 0;
13244db7dd1bSJoe Talbott }
13254db7dd1bSJoe Talbott
13267370bffcSMatthew Dillon static void
wpi_free_tx_ring(struct wpi_softc * sc,struct wpi_tx_ring * ring)13277370bffcSMatthew Dillon wpi_free_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring)
13287370bffcSMatthew Dillon {
13297370bffcSMatthew Dillon int i;
13307370bffcSMatthew Dillon
13317370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
13327370bffcSMatthew Dillon
13337370bffcSMatthew Dillon wpi_dma_contig_free(&ring->desc_dma);
13347370bffcSMatthew Dillon wpi_dma_contig_free(&ring->cmd_dma);
13357370bffcSMatthew Dillon
13367370bffcSMatthew Dillon for (i = 0; i < WPI_TX_RING_COUNT; i++) {
13377370bffcSMatthew Dillon struct wpi_tx_data *data = &ring->data[i];
13387370bffcSMatthew Dillon
13397370bffcSMatthew Dillon if (data->m != NULL) {
13407370bffcSMatthew Dillon bus_dmamap_sync(ring->data_dmat, data->map,
13417370bffcSMatthew Dillon BUS_DMASYNC_POSTWRITE);
13427370bffcSMatthew Dillon bus_dmamap_unload(ring->data_dmat, data->map);
13437370bffcSMatthew Dillon m_freem(data->m);
13447370bffcSMatthew Dillon }
13457370bffcSMatthew Dillon if (data->map != NULL)
13467370bffcSMatthew Dillon bus_dmamap_destroy(ring->data_dmat, data->map);
13477370bffcSMatthew Dillon }
13487370bffcSMatthew Dillon if (ring->data_dmat != NULL) {
13494db7dd1bSJoe Talbott bus_dma_tag_destroy(ring->data_dmat);
13507370bffcSMatthew Dillon ring->data_dmat = NULL;
13517370bffcSMatthew Dillon }
13524db7dd1bSJoe Talbott }
13534db7dd1bSJoe Talbott
13547370bffcSMatthew Dillon /*
13557370bffcSMatthew Dillon * Extract various information from EEPROM.
13567370bffcSMatthew Dillon */
13574db7dd1bSJoe Talbott static int
wpi_read_eeprom(struct wpi_softc * sc,uint8_t macaddr[IEEE80211_ADDR_LEN])13587370bffcSMatthew Dillon wpi_read_eeprom(struct wpi_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN])
13594db7dd1bSJoe Talbott {
13607370bffcSMatthew Dillon #define WPI_CHK(res) do { \
13617370bffcSMatthew Dillon if ((error = res) != 0) \
13627370bffcSMatthew Dillon goto fail; \
13637370bffcSMatthew Dillon } while (0)
13647370bffcSMatthew Dillon uint8_t i;
13657370bffcSMatthew Dillon int error;
13664db7dd1bSJoe Talbott
13677370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
13687370bffcSMatthew Dillon
13697370bffcSMatthew Dillon /* Adapter has to be powered on for EEPROM access to work. */
13707370bffcSMatthew Dillon if ((error = wpi_apm_init(sc)) != 0) {
13717370bffcSMatthew Dillon device_printf(sc->sc_dev,
13727370bffcSMatthew Dillon "%s: could not power ON adapter, error %d\n", __func__,
13737370bffcSMatthew Dillon error);
13747370bffcSMatthew Dillon return error;
13757370bffcSMatthew Dillon }
13767370bffcSMatthew Dillon
13777370bffcSMatthew Dillon if ((WPI_READ(sc, WPI_EEPROM_GP) & 0x6) == 0) {
13787370bffcSMatthew Dillon device_printf(sc->sc_dev, "bad EEPROM signature\n");
13797370bffcSMatthew Dillon error = EIO;
13807370bffcSMatthew Dillon goto fail;
13817370bffcSMatthew Dillon }
13827370bffcSMatthew Dillon /* Clear HW ownership of EEPROM. */
13837370bffcSMatthew Dillon WPI_CLRBITS(sc, WPI_EEPROM_GP, WPI_EEPROM_GP_IF_OWNER);
13847370bffcSMatthew Dillon
13857370bffcSMatthew Dillon /* Read the hardware capabilities, revision and SKU type. */
13867370bffcSMatthew Dillon WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_SKU_CAP, &sc->cap,
13877370bffcSMatthew Dillon sizeof(sc->cap)));
13887370bffcSMatthew Dillon WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_REVISION, &sc->rev,
13897370bffcSMatthew Dillon sizeof(sc->rev)));
13907370bffcSMatthew Dillon WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_TYPE, &sc->type,
13917370bffcSMatthew Dillon sizeof(sc->type)));
13927370bffcSMatthew Dillon
13937370bffcSMatthew Dillon sc->rev = le16toh(sc->rev);
13947370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_EEPROM, "cap=%x rev=%x type=%x\n", sc->cap,
13957370bffcSMatthew Dillon sc->rev, sc->type);
13967370bffcSMatthew Dillon
13977370bffcSMatthew Dillon /* Read the regulatory domain (4 ASCII characters.) */
13987370bffcSMatthew Dillon WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_DOMAIN, sc->domain,
13997370bffcSMatthew Dillon sizeof(sc->domain)));
14007370bffcSMatthew Dillon
14017370bffcSMatthew Dillon /* Read MAC address. */
14027370bffcSMatthew Dillon WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_MAC, macaddr,
14037370bffcSMatthew Dillon IEEE80211_ADDR_LEN));
14047370bffcSMatthew Dillon
14057370bffcSMatthew Dillon /* Read the list of authorized channels. */
14067370bffcSMatthew Dillon for (i = 0; i < WPI_CHAN_BANDS_COUNT; i++)
14077370bffcSMatthew Dillon WPI_CHK(wpi_read_eeprom_channels(sc, i));
14087370bffcSMatthew Dillon
14097370bffcSMatthew Dillon /* Read the list of TX power groups. */
14107370bffcSMatthew Dillon for (i = 0; i < WPI_POWER_GROUPS_COUNT; i++)
14117370bffcSMatthew Dillon WPI_CHK(wpi_read_eeprom_group(sc, i));
14127370bffcSMatthew Dillon
14137370bffcSMatthew Dillon fail: wpi_apm_stop(sc); /* Power OFF adapter. */
14147370bffcSMatthew Dillon
14157370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, error ? TRACE_STR_END_ERR : TRACE_STR_END,
14167370bffcSMatthew Dillon __func__);
14177370bffcSMatthew Dillon
14187370bffcSMatthew Dillon return error;
14197370bffcSMatthew Dillon #undef WPI_CHK
14207370bffcSMatthew Dillon }
14217370bffcSMatthew Dillon
14227370bffcSMatthew Dillon /*
14237370bffcSMatthew Dillon * Translate EEPROM flags to net80211.
14247370bffcSMatthew Dillon */
14257370bffcSMatthew Dillon static uint32_t
wpi_eeprom_channel_flags(struct wpi_eeprom_chan * channel)14267370bffcSMatthew Dillon wpi_eeprom_channel_flags(struct wpi_eeprom_chan *channel)
14277370bffcSMatthew Dillon {
14287370bffcSMatthew Dillon uint32_t nflags;
14297370bffcSMatthew Dillon
14307370bffcSMatthew Dillon nflags = 0;
14317370bffcSMatthew Dillon if ((channel->flags & WPI_EEPROM_CHAN_ACTIVE) == 0)
14327370bffcSMatthew Dillon nflags |= IEEE80211_CHAN_PASSIVE;
14337370bffcSMatthew Dillon if ((channel->flags & WPI_EEPROM_CHAN_IBSS) == 0)
14347370bffcSMatthew Dillon nflags |= IEEE80211_CHAN_NOADHOC;
14357370bffcSMatthew Dillon if (channel->flags & WPI_EEPROM_CHAN_RADAR) {
14367370bffcSMatthew Dillon nflags |= IEEE80211_CHAN_DFS;
14377370bffcSMatthew Dillon /* XXX apparently IBSS may still be marked */
14387370bffcSMatthew Dillon nflags |= IEEE80211_CHAN_NOADHOC;
14397370bffcSMatthew Dillon }
14407370bffcSMatthew Dillon
14417370bffcSMatthew Dillon /* XXX HOSTAP uses WPI_MODE_IBSS */
14427370bffcSMatthew Dillon if (nflags & IEEE80211_CHAN_NOADHOC)
14437370bffcSMatthew Dillon nflags |= IEEE80211_CHAN_NOHOSTAP;
14447370bffcSMatthew Dillon
14457370bffcSMatthew Dillon return nflags;
14467370bffcSMatthew Dillon }
14477370bffcSMatthew Dillon
14487370bffcSMatthew Dillon static void
wpi_read_eeprom_band(struct wpi_softc * sc,uint8_t n,int maxchans,int * nchans,struct ieee80211_channel chans[])14497370bffcSMatthew Dillon wpi_read_eeprom_band(struct wpi_softc *sc, uint8_t n, int maxchans,
14507370bffcSMatthew Dillon int *nchans, struct ieee80211_channel chans[])
14517370bffcSMatthew Dillon {
14527370bffcSMatthew Dillon struct wpi_eeprom_chan *channels = sc->eeprom_channels[n];
14537370bffcSMatthew Dillon const struct wpi_chan_band *band = &wpi_bands[n];
14547370bffcSMatthew Dillon uint32_t nflags;
14557370bffcSMatthew Dillon uint8_t bands[IEEE80211_MODE_BYTES];
14567370bffcSMatthew Dillon uint8_t chan, i;
14577370bffcSMatthew Dillon int error;
14587370bffcSMatthew Dillon
14597370bffcSMatthew Dillon memset(bands, 0, sizeof(bands));
14607370bffcSMatthew Dillon
14617370bffcSMatthew Dillon if (n == 0) {
14627370bffcSMatthew Dillon setbit(bands, IEEE80211_MODE_11B);
14637370bffcSMatthew Dillon setbit(bands, IEEE80211_MODE_11G);
14647370bffcSMatthew Dillon } else
14657370bffcSMatthew Dillon setbit(bands, IEEE80211_MODE_11A);
14667370bffcSMatthew Dillon
14677370bffcSMatthew Dillon for (i = 0; i < band->nchan; i++) {
14687370bffcSMatthew Dillon if (!(channels[i].flags & WPI_EEPROM_CHAN_VALID)) {
14697370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_EEPROM,
14707370bffcSMatthew Dillon "Channel Not Valid: %d, band %d\n",
14717370bffcSMatthew Dillon band->chan[i],n);
14727370bffcSMatthew Dillon continue;
14737370bffcSMatthew Dillon }
14747370bffcSMatthew Dillon
14757370bffcSMatthew Dillon chan = band->chan[i];
14767370bffcSMatthew Dillon nflags = wpi_eeprom_channel_flags(&channels[i]);
14777370bffcSMatthew Dillon error = ieee80211_add_channel(chans, maxchans, nchans,
14787370bffcSMatthew Dillon chan, 0, channels[i].maxpwr, nflags, bands);
14797370bffcSMatthew Dillon if (error != 0)
14807370bffcSMatthew Dillon break;
14817370bffcSMatthew Dillon
14827370bffcSMatthew Dillon /* Save maximum allowed TX power for this channel. */
14837370bffcSMatthew Dillon sc->maxpwr[chan] = channels[i].maxpwr;
14847370bffcSMatthew Dillon
14857370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_EEPROM,
14867370bffcSMatthew Dillon "adding chan %d flags=0x%x maxpwr=%d, offset %d\n",
14877370bffcSMatthew Dillon chan, channels[i].flags, sc->maxpwr[chan], *nchans);
14887370bffcSMatthew Dillon }
14897370bffcSMatthew Dillon }
14907370bffcSMatthew Dillon
14917370bffcSMatthew Dillon /**
14927370bffcSMatthew Dillon * Read the eeprom to find out what channels are valid for the given
14937370bffcSMatthew Dillon * band and update net80211 with what we find.
14947370bffcSMatthew Dillon */
14957370bffcSMatthew Dillon static int
wpi_read_eeprom_channels(struct wpi_softc * sc,uint8_t n)14967370bffcSMatthew Dillon wpi_read_eeprom_channels(struct wpi_softc *sc, uint8_t n)
14977370bffcSMatthew Dillon {
14987370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
14997370bffcSMatthew Dillon const struct wpi_chan_band *band = &wpi_bands[n];
15007370bffcSMatthew Dillon int error;
15017370bffcSMatthew Dillon
15027370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
15037370bffcSMatthew Dillon
15047370bffcSMatthew Dillon error = wpi_read_prom_data(sc, band->addr, &sc->eeprom_channels[n],
15057370bffcSMatthew Dillon band->nchan * sizeof (struct wpi_eeprom_chan));
15067370bffcSMatthew Dillon if (error != 0) {
15077370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__);
15087370bffcSMatthew Dillon return error;
15097370bffcSMatthew Dillon }
15107370bffcSMatthew Dillon
15117370bffcSMatthew Dillon wpi_read_eeprom_band(sc, n, IEEE80211_CHAN_MAX, &ic->ic_nchans,
15127370bffcSMatthew Dillon ic->ic_channels);
15137370bffcSMatthew Dillon
15147370bffcSMatthew Dillon ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
15157370bffcSMatthew Dillon
15167370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
15177370bffcSMatthew Dillon
15187370bffcSMatthew Dillon return 0;
15197370bffcSMatthew Dillon }
15207370bffcSMatthew Dillon
15217370bffcSMatthew Dillon static struct wpi_eeprom_chan *
wpi_find_eeprom_channel(struct wpi_softc * sc,struct ieee80211_channel * c)15227370bffcSMatthew Dillon wpi_find_eeprom_channel(struct wpi_softc *sc, struct ieee80211_channel *c)
15237370bffcSMatthew Dillon {
15247370bffcSMatthew Dillon int i, j;
15257370bffcSMatthew Dillon
15267370bffcSMatthew Dillon for (j = 0; j < WPI_CHAN_BANDS_COUNT; j++)
15277370bffcSMatthew Dillon for (i = 0; i < wpi_bands[j].nchan; i++)
15287370bffcSMatthew Dillon if (wpi_bands[j].chan[i] == c->ic_ieee &&
15297370bffcSMatthew Dillon ((j == 0) ^ IEEE80211_IS_CHAN_A(c)) == 1)
15307370bffcSMatthew Dillon return &sc->eeprom_channels[j][i];
15317370bffcSMatthew Dillon
15327370bffcSMatthew Dillon return NULL;
15337370bffcSMatthew Dillon }
15347370bffcSMatthew Dillon
15357370bffcSMatthew Dillon static void
wpi_getradiocaps(struct ieee80211com * ic,int maxchans,int * nchans,struct ieee80211_channel chans[])15367370bffcSMatthew Dillon wpi_getradiocaps(struct ieee80211com *ic,
15377370bffcSMatthew Dillon int maxchans, int *nchans, struct ieee80211_channel chans[])
15387370bffcSMatthew Dillon {
15397370bffcSMatthew Dillon struct wpi_softc *sc = ic->ic_softc;
15407370bffcSMatthew Dillon int i;
15417370bffcSMatthew Dillon
15427370bffcSMatthew Dillon /* Parse the list of authorized channels. */
15437370bffcSMatthew Dillon for (i = 0; i < WPI_CHAN_BANDS_COUNT && *nchans < maxchans; i++)
15447370bffcSMatthew Dillon wpi_read_eeprom_band(sc, i, maxchans, nchans, chans);
15457370bffcSMatthew Dillon }
15467370bffcSMatthew Dillon
15477370bffcSMatthew Dillon /*
15487370bffcSMatthew Dillon * Enforce flags read from EEPROM.
15497370bffcSMatthew Dillon */
15507370bffcSMatthew Dillon static int
wpi_setregdomain(struct ieee80211com * ic,struct ieee80211_regdomain * rd,int nchan,struct ieee80211_channel chans[])15517370bffcSMatthew Dillon wpi_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd,
15527370bffcSMatthew Dillon int nchan, struct ieee80211_channel chans[])
15537370bffcSMatthew Dillon {
15547370bffcSMatthew Dillon struct wpi_softc *sc = ic->ic_softc;
15557370bffcSMatthew Dillon int i;
15567370bffcSMatthew Dillon
15577370bffcSMatthew Dillon for (i = 0; i < nchan; i++) {
15587370bffcSMatthew Dillon struct ieee80211_channel *c = &chans[i];
15597370bffcSMatthew Dillon struct wpi_eeprom_chan *channel;
15607370bffcSMatthew Dillon
15617370bffcSMatthew Dillon channel = wpi_find_eeprom_channel(sc, c);
15627370bffcSMatthew Dillon if (channel == NULL) {
15637370bffcSMatthew Dillon ic_printf(ic, "%s: invalid channel %u freq %u/0x%x\n",
15647370bffcSMatthew Dillon __func__, c->ic_ieee, c->ic_freq, c->ic_flags);
15657370bffcSMatthew Dillon return EINVAL;
15667370bffcSMatthew Dillon }
15677370bffcSMatthew Dillon c->ic_flags |= wpi_eeprom_channel_flags(channel);
15687370bffcSMatthew Dillon }
15694db7dd1bSJoe Talbott
15704db7dd1bSJoe Talbott return 0;
15714db7dd1bSJoe Talbott }
15724db7dd1bSJoe Talbott
15734db7dd1bSJoe Talbott static int
wpi_read_eeprom_group(struct wpi_softc * sc,uint8_t n)15747370bffcSMatthew Dillon wpi_read_eeprom_group(struct wpi_softc *sc, uint8_t n)
15754db7dd1bSJoe Talbott {
15767370bffcSMatthew Dillon struct wpi_power_group *group = &sc->groups[n];
15777370bffcSMatthew Dillon struct wpi_eeprom_group rgroup;
15787370bffcSMatthew Dillon int i, error;
15794db7dd1bSJoe Talbott
15807370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
15817370bffcSMatthew Dillon
15827370bffcSMatthew Dillon if ((error = wpi_read_prom_data(sc, WPI_EEPROM_POWER_GRP + n * 32,
15837370bffcSMatthew Dillon &rgroup, sizeof rgroup)) != 0) {
15847370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__);
15857370bffcSMatthew Dillon return error;
15867370bffcSMatthew Dillon }
15877370bffcSMatthew Dillon
15887370bffcSMatthew Dillon /* Save TX power group information. */
15897370bffcSMatthew Dillon group->chan = rgroup.chan;
15907370bffcSMatthew Dillon group->maxpwr = rgroup.maxpwr;
15917370bffcSMatthew Dillon /* Retrieve temperature at which the samples were taken. */
15927370bffcSMatthew Dillon group->temp = (int16_t)le16toh(rgroup.temp);
15937370bffcSMatthew Dillon
15947370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_EEPROM,
15957370bffcSMatthew Dillon "power group %d: chan=%d maxpwr=%d temp=%d\n", n, group->chan,
15967370bffcSMatthew Dillon group->maxpwr, group->temp);
15977370bffcSMatthew Dillon
15987370bffcSMatthew Dillon for (i = 0; i < WPI_SAMPLES_COUNT; i++) {
15997370bffcSMatthew Dillon group->samples[i].index = rgroup.samples[i].index;
16007370bffcSMatthew Dillon group->samples[i].power = rgroup.samples[i].power;
16017370bffcSMatthew Dillon
16027370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_EEPROM,
16037370bffcSMatthew Dillon "\tsample %d: index=%d power=%d\n", i,
16047370bffcSMatthew Dillon group->samples[i].index, group->samples[i].power);
16057370bffcSMatthew Dillon }
16067370bffcSMatthew Dillon
16077370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
16087370bffcSMatthew Dillon
16094db7dd1bSJoe Talbott return 0;
16104db7dd1bSJoe Talbott }
16114db7dd1bSJoe Talbott
16127370bffcSMatthew Dillon static __inline uint8_t
wpi_add_node_entry_adhoc(struct wpi_softc * sc)16137370bffcSMatthew Dillon wpi_add_node_entry_adhoc(struct wpi_softc *sc)
16144db7dd1bSJoe Talbott {
16157370bffcSMatthew Dillon uint8_t newid = WPI_ID_IBSS_MIN;
16164db7dd1bSJoe Talbott
16177370bffcSMatthew Dillon for (; newid <= WPI_ID_IBSS_MAX; newid++) {
16187370bffcSMatthew Dillon if ((sc->nodesmsk & (1 << newid)) == 0) {
16197370bffcSMatthew Dillon sc->nodesmsk |= 1 << newid;
16207370bffcSMatthew Dillon return newid;
16217370bffcSMatthew Dillon }
16227370bffcSMatthew Dillon }
16234db7dd1bSJoe Talbott
16247370bffcSMatthew Dillon return WPI_ID_UNDEFINED;
16257370bffcSMatthew Dillon }
16267370bffcSMatthew Dillon
16277370bffcSMatthew Dillon static __inline uint8_t
wpi_add_node_entry_sta(struct wpi_softc * sc)16287370bffcSMatthew Dillon wpi_add_node_entry_sta(struct wpi_softc *sc)
16297370bffcSMatthew Dillon {
16307370bffcSMatthew Dillon sc->nodesmsk |= 1 << WPI_ID_BSS;
16317370bffcSMatthew Dillon
16327370bffcSMatthew Dillon return WPI_ID_BSS;
16337370bffcSMatthew Dillon }
16347370bffcSMatthew Dillon
16357370bffcSMatthew Dillon static __inline int
wpi_check_node_entry(struct wpi_softc * sc,uint8_t id)16367370bffcSMatthew Dillon wpi_check_node_entry(struct wpi_softc *sc, uint8_t id)
16377370bffcSMatthew Dillon {
16387370bffcSMatthew Dillon if (id == WPI_ID_UNDEFINED)
16394db7dd1bSJoe Talbott return 0;
16407370bffcSMatthew Dillon
16417370bffcSMatthew Dillon return (sc->nodesmsk >> id) & 1;
16427370bffcSMatthew Dillon }
16437370bffcSMatthew Dillon
16447370bffcSMatthew Dillon static __inline void
wpi_clear_node_table(struct wpi_softc * sc)16457370bffcSMatthew Dillon wpi_clear_node_table(struct wpi_softc *sc)
16467370bffcSMatthew Dillon {
16477370bffcSMatthew Dillon sc->nodesmsk = 0;
16487370bffcSMatthew Dillon }
16497370bffcSMatthew Dillon
16507370bffcSMatthew Dillon static __inline void
wpi_del_node_entry(struct wpi_softc * sc,uint8_t id)16517370bffcSMatthew Dillon wpi_del_node_entry(struct wpi_softc *sc, uint8_t id)
16527370bffcSMatthew Dillon {
16537370bffcSMatthew Dillon sc->nodesmsk &= ~(1 << id);
16547370bffcSMatthew Dillon }
16557370bffcSMatthew Dillon
16567370bffcSMatthew Dillon static struct ieee80211_node *
wpi_node_alloc(struct ieee80211vap * vap,const uint8_t mac[IEEE80211_ADDR_LEN])16577370bffcSMatthew Dillon wpi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
16587370bffcSMatthew Dillon {
16597370bffcSMatthew Dillon struct wpi_node *wn;
16607370bffcSMatthew Dillon
16617370bffcSMatthew Dillon wn = kmalloc(sizeof (struct wpi_node), M_80211_NODE,
16627370bffcSMatthew Dillon M_INTWAIT | M_ZERO);
16637370bffcSMatthew Dillon
16647370bffcSMatthew Dillon if (wn == NULL)
16657370bffcSMatthew Dillon return NULL;
16667370bffcSMatthew Dillon
16677370bffcSMatthew Dillon wn->id = WPI_ID_UNDEFINED;
16687370bffcSMatthew Dillon
16697370bffcSMatthew Dillon return &wn->ni;
16707370bffcSMatthew Dillon }
16717370bffcSMatthew Dillon
16727370bffcSMatthew Dillon static void
wpi_node_free(struct ieee80211_node * ni)16737370bffcSMatthew Dillon wpi_node_free(struct ieee80211_node *ni)
16747370bffcSMatthew Dillon {
16757370bffcSMatthew Dillon struct wpi_softc *sc = ni->ni_ic->ic_softc;
16767370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
16777370bffcSMatthew Dillon
16787370bffcSMatthew Dillon if (wn->id != WPI_ID_UNDEFINED) {
16797370bffcSMatthew Dillon WPI_NT_LOCK(sc);
16807370bffcSMatthew Dillon if (wpi_check_node_entry(sc, wn->id)) {
16817370bffcSMatthew Dillon wpi_del_node_entry(sc, wn->id);
16827370bffcSMatthew Dillon wpi_del_node(sc, ni);
16837370bffcSMatthew Dillon }
16847370bffcSMatthew Dillon WPI_NT_UNLOCK(sc);
16857370bffcSMatthew Dillon }
16867370bffcSMatthew Dillon
16877370bffcSMatthew Dillon sc->sc_node_free(ni);
16887370bffcSMatthew Dillon }
16897370bffcSMatthew Dillon
16907370bffcSMatthew Dillon static __inline int
wpi_check_bss_filter(struct wpi_softc * sc)16917370bffcSMatthew Dillon wpi_check_bss_filter(struct wpi_softc *sc)
16927370bffcSMatthew Dillon {
16937370bffcSMatthew Dillon return (sc->rxon.filter & htole32(WPI_FILTER_BSS)) != 0;
16947370bffcSMatthew Dillon }
16957370bffcSMatthew Dillon
16967370bffcSMatthew Dillon static void
wpi_ibss_recv_mgmt(struct ieee80211_node * ni,struct mbuf * m,int subtype,const struct ieee80211_rx_stats * rxs,int rssi,int nf)16977370bffcSMatthew Dillon wpi_ibss_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype,
16987370bffcSMatthew Dillon const struct ieee80211_rx_stats *rxs,
16997370bffcSMatthew Dillon int rssi, int nf)
17007370bffcSMatthew Dillon {
17017370bffcSMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
17027370bffcSMatthew Dillon struct wpi_softc *sc = vap->iv_ic->ic_softc;
17037370bffcSMatthew Dillon struct wpi_vap *wvp = WPI_VAP(vap);
17047370bffcSMatthew Dillon uint64_t ni_tstamp, rx_tstamp;
17057370bffcSMatthew Dillon
17067370bffcSMatthew Dillon wvp->wv_recv_mgmt(ni, m, subtype, rxs, rssi, nf);
17077370bffcSMatthew Dillon
17087370bffcSMatthew Dillon if (vap->iv_state == IEEE80211_S_RUN &&
17097370bffcSMatthew Dillon (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
17107370bffcSMatthew Dillon subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) {
17117370bffcSMatthew Dillon ni_tstamp = le64toh(ni->ni_tstamp.tsf);
17127370bffcSMatthew Dillon rx_tstamp = le64toh(sc->rx_tstamp);
17137370bffcSMatthew Dillon
17147370bffcSMatthew Dillon if (ni_tstamp >= rx_tstamp) {
17157370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_STATE,
17167370bffcSMatthew Dillon "ibss merge, tsf %ju tstamp %ju\n",
17177370bffcSMatthew Dillon (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp);
17187370bffcSMatthew Dillon (void) ieee80211_ibss_merge(ni);
17197370bffcSMatthew Dillon }
17207370bffcSMatthew Dillon }
17217370bffcSMatthew Dillon }
17227370bffcSMatthew Dillon
17237370bffcSMatthew Dillon static void
wpi_restore_node(void * arg,struct ieee80211_node * ni)17247370bffcSMatthew Dillon wpi_restore_node(void *arg, struct ieee80211_node *ni)
17257370bffcSMatthew Dillon {
17267370bffcSMatthew Dillon struct wpi_softc *sc = arg;
17277370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
17287370bffcSMatthew Dillon int error;
17297370bffcSMatthew Dillon
17307370bffcSMatthew Dillon WPI_NT_LOCK(sc);
17317370bffcSMatthew Dillon if (wn->id != WPI_ID_UNDEFINED) {
17327370bffcSMatthew Dillon wn->id = WPI_ID_UNDEFINED;
17337370bffcSMatthew Dillon if ((error = wpi_add_ibss_node(sc, ni)) != 0) {
17347370bffcSMatthew Dillon device_printf(sc->sc_dev,
17357370bffcSMatthew Dillon "%s: could not add IBSS node, error %d\n",
17367370bffcSMatthew Dillon __func__, error);
17377370bffcSMatthew Dillon }
17387370bffcSMatthew Dillon }
17397370bffcSMatthew Dillon WPI_NT_UNLOCK(sc);
17407370bffcSMatthew Dillon }
17417370bffcSMatthew Dillon
17427370bffcSMatthew Dillon static void
wpi_restore_node_table(struct wpi_softc * sc,struct wpi_vap * wvp)17437370bffcSMatthew Dillon wpi_restore_node_table(struct wpi_softc *sc, struct wpi_vap *wvp)
17447370bffcSMatthew Dillon {
17457370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
17467370bffcSMatthew Dillon
17477370bffcSMatthew Dillon /* Set group keys once. */
17487370bffcSMatthew Dillon WPI_NT_LOCK(sc);
17497370bffcSMatthew Dillon wvp->wv_gtk = 0;
17507370bffcSMatthew Dillon WPI_NT_UNLOCK(sc);
17517370bffcSMatthew Dillon
17527370bffcSMatthew Dillon ieee80211_iterate_nodes(&ic->ic_sta, wpi_restore_node, sc);
17537370bffcSMatthew Dillon ieee80211_crypto_reload_keys(ic);
17544db7dd1bSJoe Talbott }
17554db7dd1bSJoe Talbott
17564db7dd1bSJoe Talbott /**
17574db7dd1bSJoe Talbott * Called by net80211 when ever there is a change to 80211 state machine
17584db7dd1bSJoe Talbott */
17594db7dd1bSJoe Talbott static int
wpi_newstate(struct ieee80211vap * vap,enum ieee80211_state nstate,int arg)17604db7dd1bSJoe Talbott wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
17614db7dd1bSJoe Talbott {
17624db7dd1bSJoe Talbott struct wpi_vap *wvp = WPI_VAP(vap);
17634db7dd1bSJoe Talbott struct ieee80211com *ic = vap->iv_ic;
17644f1aaf2fSImre Vadász struct wpi_softc *sc = ic->ic_softc;
17657370bffcSMatthew Dillon int error = 0;
17664db7dd1bSJoe Talbott
17677370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
17687370bffcSMatthew Dillon
17697370bffcSMatthew Dillon WPI_TXQ_LOCK(sc);
17707370bffcSMatthew Dillon if (nstate > IEEE80211_S_INIT && sc->sc_running == 0) {
17717370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__);
17727370bffcSMatthew Dillon WPI_TXQ_UNLOCK(sc);
17737370bffcSMatthew Dillon
17747370bffcSMatthew Dillon return ENXIO;
17757370bffcSMatthew Dillon }
17767370bffcSMatthew Dillon WPI_TXQ_UNLOCK(sc);
17777370bffcSMatthew Dillon
17787370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_STATE, "%s: %s -> %s\n", __func__,
17794db7dd1bSJoe Talbott ieee80211_state_name[vap->iv_state],
17807370bffcSMatthew Dillon ieee80211_state_name[nstate]);
17814db7dd1bSJoe Talbott
17827370bffcSMatthew Dillon if (vap->iv_state == IEEE80211_S_RUN && nstate < IEEE80211_S_RUN) {
17837370bffcSMatthew Dillon if ((error = wpi_set_pslevel(sc, 0, 0, 1)) != 0) {
1784d0c50e91SJohannes Hofmann device_printf(sc->sc_dev,
17857370bffcSMatthew Dillon "%s: could not set power saving level\n",
17867370bffcSMatthew Dillon __func__);
17877370bffcSMatthew Dillon return error;
17887370bffcSMatthew Dillon }
17897370bffcSMatthew Dillon
17907370bffcSMatthew Dillon wpi_set_led(sc, WPI_LED_LINK, 1, 0);
17917370bffcSMatthew Dillon }
17927370bffcSMatthew Dillon
17937370bffcSMatthew Dillon switch (nstate) {
17947370bffcSMatthew Dillon case IEEE80211_S_SCAN:
17957370bffcSMatthew Dillon WPI_RXON_LOCK(sc);
17967370bffcSMatthew Dillon if (wpi_check_bss_filter(sc) != 0) {
17977370bffcSMatthew Dillon sc->rxon.filter &= ~htole32(WPI_FILTER_BSS);
17987370bffcSMatthew Dillon if ((error = wpi_send_rxon(sc, 0, 1)) != 0) {
17997370bffcSMatthew Dillon device_printf(sc->sc_dev,
18007370bffcSMatthew Dillon "%s: could not send RXON\n", __func__);
1801d0c50e91SJohannes Hofmann }
1802d0c50e91SJohannes Hofmann }
18037370bffcSMatthew Dillon WPI_RXON_UNLOCK(sc);
18047370bffcSMatthew Dillon break;
18057370bffcSMatthew Dillon
18067370bffcSMatthew Dillon case IEEE80211_S_ASSOC:
18077370bffcSMatthew Dillon if (vap->iv_state != IEEE80211_S_RUN)
18087370bffcSMatthew Dillon break;
18097370bffcSMatthew Dillon /* FALLTHROUGH */
18107370bffcSMatthew Dillon case IEEE80211_S_AUTH:
18117370bffcSMatthew Dillon /*
18127370bffcSMatthew Dillon * NB: do not optimize AUTH -> AUTH state transmission -
18137370bffcSMatthew Dillon * this will break powersave with non-QoS AP!
18147370bffcSMatthew Dillon */
18157370bffcSMatthew Dillon
1816d0c50e91SJohannes Hofmann /*
1817d0c50e91SJohannes Hofmann * The node must be registered in the firmware before auth.
1818d0c50e91SJohannes Hofmann * Also the associd must be cleared on RUN -> ASSOC
1819d0c50e91SJohannes Hofmann * transitions.
1820d0c50e91SJohannes Hofmann */
18217370bffcSMatthew Dillon if ((error = wpi_auth(sc, vap)) != 0) {
18224db7dd1bSJoe Talbott device_printf(sc->sc_dev,
18237370bffcSMatthew Dillon "%s: could not move to AUTH state, error %d\n",
18244db7dd1bSJoe Talbott __func__, error);
18254db7dd1bSJoe Talbott }
18267370bffcSMatthew Dillon break;
18277370bffcSMatthew Dillon
18287370bffcSMatthew Dillon case IEEE80211_S_RUN:
18297370bffcSMatthew Dillon /*
18307370bffcSMatthew Dillon * RUN -> RUN transition:
18317370bffcSMatthew Dillon * STA mode: Just restart the timers.
18327370bffcSMatthew Dillon * IBSS mode: Process IBSS merge.
18337370bffcSMatthew Dillon */
18347370bffcSMatthew Dillon if (vap->iv_state == IEEE80211_S_RUN) {
18357370bffcSMatthew Dillon if (vap->iv_opmode != IEEE80211_M_IBSS) {
18367370bffcSMatthew Dillon WPI_RXON_LOCK(sc);
1837d0c50e91SJohannes Hofmann wpi_calib_timeout(sc);
18387370bffcSMatthew Dillon WPI_RXON_UNLOCK(sc);
18394db7dd1bSJoe Talbott break;
18407370bffcSMatthew Dillon } else {
18414db7dd1bSJoe Talbott /*
18427370bffcSMatthew Dillon * Drop the BSS_FILTER bit
18437370bffcSMatthew Dillon * (there is no another way to change bssid).
18444db7dd1bSJoe Talbott */
18457370bffcSMatthew Dillon WPI_RXON_LOCK(sc);
18467370bffcSMatthew Dillon sc->rxon.filter &= ~htole32(WPI_FILTER_BSS);
18477370bffcSMatthew Dillon if ((error = wpi_send_rxon(sc, 0, 1)) != 0) {
18484db7dd1bSJoe Talbott device_printf(sc->sc_dev,
18497370bffcSMatthew Dillon "%s: could not send RXON\n",
18507370bffcSMatthew Dillon __func__);
18514db7dd1bSJoe Talbott }
18527370bffcSMatthew Dillon WPI_RXON_UNLOCK(sc);
18534db7dd1bSJoe Talbott
18547370bffcSMatthew Dillon /* Restore all what was lost. */
18557370bffcSMatthew Dillon wpi_restore_node_table(sc, wvp);
18564db7dd1bSJoe Talbott
18577370bffcSMatthew Dillon /* XXX set conditionally? */
18587370bffcSMatthew Dillon wpi_updateedca(ic);
18594db7dd1bSJoe Talbott }
18604db7dd1bSJoe Talbott }
18614db7dd1bSJoe Talbott
18624db7dd1bSJoe Talbott /*
18637370bffcSMatthew Dillon * !RUN -> RUN requires setting the association id
18647370bffcSMatthew Dillon * which is done with a firmware cmd. We also defer
18657370bffcSMatthew Dillon * starting the timers until that work is done.
18664db7dd1bSJoe Talbott */
18677370bffcSMatthew Dillon if ((error = wpi_run(sc, vap)) != 0) {
18684db7dd1bSJoe Talbott device_printf(sc->sc_dev,
18697370bffcSMatthew Dillon "%s: could not move to RUN state\n", __func__);
18704db7dd1bSJoe Talbott }
18714db7dd1bSJoe Talbott break;
18724db7dd1bSJoe Talbott
18737370bffcSMatthew Dillon default:
18744db7dd1bSJoe Talbott break;
18754db7dd1bSJoe Talbott }
18767370bffcSMatthew Dillon if (error != 0) {
18777370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__);
18787370bffcSMatthew Dillon return error;
18794db7dd1bSJoe Talbott }
18804db7dd1bSJoe Talbott
18817370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
18824db7dd1bSJoe Talbott
18837370bffcSMatthew Dillon return wvp->wv_newstate(vap, nstate, arg);
18844db7dd1bSJoe Talbott }
18854db7dd1bSJoe Talbott
18864db7dd1bSJoe Talbott static void
wpi_calib_timeout(void * arg)18877370bffcSMatthew Dillon wpi_calib_timeout(void *arg)
18884db7dd1bSJoe Talbott {
18894db7dd1bSJoe Talbott struct wpi_softc *sc = arg;
18904db7dd1bSJoe Talbott
18917370bffcSMatthew Dillon if (wpi_check_bss_filter(sc) == 0)
18924db7dd1bSJoe Talbott return;
18937370bffcSMatthew Dillon
18947370bffcSMatthew Dillon wpi_power_calibration(sc);
18957370bffcSMatthew Dillon
18967370bffcSMatthew Dillon callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc);
18974db7dd1bSJoe Talbott }
18984db7dd1bSJoe Talbott
18997370bffcSMatthew Dillon static __inline uint8_t
rate2plcp(const uint8_t rate)19007370bffcSMatthew Dillon rate2plcp(const uint8_t rate)
19014db7dd1bSJoe Talbott {
19024db7dd1bSJoe Talbott switch (rate) {
19034db7dd1bSJoe Talbott case 12: return 0xd;
19044db7dd1bSJoe Talbott case 18: return 0xf;
19054db7dd1bSJoe Talbott case 24: return 0x5;
19064db7dd1bSJoe Talbott case 36: return 0x7;
19074db7dd1bSJoe Talbott case 48: return 0x9;
19084db7dd1bSJoe Talbott case 72: return 0xb;
19094db7dd1bSJoe Talbott case 96: return 0x1;
19104db7dd1bSJoe Talbott case 108: return 0x3;
19117370bffcSMatthew Dillon case 2: return 10;
19127370bffcSMatthew Dillon case 4: return 20;
19137370bffcSMatthew Dillon case 11: return 55;
19147370bffcSMatthew Dillon case 22: return 110;
19154db7dd1bSJoe Talbott default: return 0;
19164db7dd1bSJoe Talbott }
19174db7dd1bSJoe Talbott }
19184db7dd1bSJoe Talbott
19197370bffcSMatthew Dillon static __inline uint8_t
plcp2rate(const uint8_t plcp)19207370bffcSMatthew Dillon plcp2rate(const uint8_t plcp)
19217370bffcSMatthew Dillon {
19227370bffcSMatthew Dillon switch (plcp) {
19237370bffcSMatthew Dillon case 0xd: return 12;
19247370bffcSMatthew Dillon case 0xf: return 18;
19257370bffcSMatthew Dillon case 0x5: return 24;
19267370bffcSMatthew Dillon case 0x7: return 36;
19277370bffcSMatthew Dillon case 0x9: return 48;
19287370bffcSMatthew Dillon case 0xb: return 72;
19297370bffcSMatthew Dillon case 0x1: return 96;
19307370bffcSMatthew Dillon case 0x3: return 108;
19317370bffcSMatthew Dillon case 10: return 2;
19327370bffcSMatthew Dillon case 20: return 4;
19337370bffcSMatthew Dillon case 55: return 11;
19347370bffcSMatthew Dillon case 110: return 22;
19357370bffcSMatthew Dillon default: return 0;
19367370bffcSMatthew Dillon }
19377370bffcSMatthew Dillon }
19387370bffcSMatthew Dillon
19397370bffcSMatthew Dillon /* Quickly determine if a given rate is CCK or OFDM. */
19404db7dd1bSJoe Talbott #define WPI_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
19414db7dd1bSJoe Talbott
19427370bffcSMatthew Dillon static void
wpi_rx_done(struct wpi_softc * sc,struct wpi_rx_desc * desc,struct wpi_rx_data * data)19437370bffcSMatthew Dillon wpi_rx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc,
19447370bffcSMatthew Dillon struct wpi_rx_data *data)
19454db7dd1bSJoe Talbott {
19467370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
19477370bffcSMatthew Dillon struct wpi_rx_ring *ring = &sc->rxq;
19487370bffcSMatthew Dillon struct wpi_rx_stat *stat;
19497370bffcSMatthew Dillon struct wpi_rx_head *head;
19507370bffcSMatthew Dillon struct wpi_rx_tail *tail;
19517370bffcSMatthew Dillon struct ieee80211_frame *wh;
19527370bffcSMatthew Dillon struct ieee80211_node *ni;
19537370bffcSMatthew Dillon struct mbuf *m, *m1;
19547370bffcSMatthew Dillon bus_addr_t paddr;
19557370bffcSMatthew Dillon uint32_t flags;
19567370bffcSMatthew Dillon uint16_t len;
19577370bffcSMatthew Dillon int error;
19587370bffcSMatthew Dillon
19597370bffcSMatthew Dillon stat = (struct wpi_rx_stat *)(desc + 1);
19607370bffcSMatthew Dillon
19617370bffcSMatthew Dillon if (__predict_false(stat->len > WPI_STAT_MAXLEN)) {
19627370bffcSMatthew Dillon device_printf(sc->sc_dev, "invalid RX statistic header\n");
19637370bffcSMatthew Dillon goto fail1;
19647370bffcSMatthew Dillon }
19657370bffcSMatthew Dillon
19667370bffcSMatthew Dillon bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
19677370bffcSMatthew Dillon head = (struct wpi_rx_head *)((caddr_t)(stat + 1) + stat->len);
19687370bffcSMatthew Dillon len = le16toh(head->len);
19697370bffcSMatthew Dillon tail = (struct wpi_rx_tail *)((caddr_t)(head + 1) + len);
19707370bffcSMatthew Dillon flags = le32toh(tail->flags);
19717370bffcSMatthew Dillon
19727370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_RECV, "%s: idx %d len %d stat len %u rssi %d"
19737370bffcSMatthew Dillon " rate %x chan %d tstamp %ju\n", __func__, ring->cur,
19747370bffcSMatthew Dillon le32toh(desc->len), len, (int8_t)stat->rssi,
19757370bffcSMatthew Dillon head->plcp, head->chan, (uintmax_t)le64toh(tail->tstamp));
19767370bffcSMatthew Dillon
19777370bffcSMatthew Dillon /* Discard frames with a bad FCS early. */
19787370bffcSMatthew Dillon if ((flags & WPI_RX_NOERROR) != WPI_RX_NOERROR) {
19797370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_RECV, "%s: RX flags error %x\n",
19807370bffcSMatthew Dillon __func__, flags);
19817370bffcSMatthew Dillon goto fail1;
19827370bffcSMatthew Dillon }
19837370bffcSMatthew Dillon /* Discard frames that are too short. */
19847370bffcSMatthew Dillon if (len < sizeof (struct ieee80211_frame_ack)) {
19857370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_RECV, "%s: frame too short: %d\n",
19867370bffcSMatthew Dillon __func__, len);
19877370bffcSMatthew Dillon goto fail1;
19887370bffcSMatthew Dillon }
19897370bffcSMatthew Dillon
19907370bffcSMatthew Dillon m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
19917370bffcSMatthew Dillon if (__predict_false(m1 == NULL)) {
19927370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_ANY, "%s: no mbuf to restock ring\n",
19937370bffcSMatthew Dillon __func__);
19947370bffcSMatthew Dillon goto fail1;
19957370bffcSMatthew Dillon }
19967370bffcSMatthew Dillon bus_dmamap_unload(ring->data_dmat, data->map);
19977370bffcSMatthew Dillon
19987370bffcSMatthew Dillon error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *),
19997370bffcSMatthew Dillon MJUMPAGESIZE, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
20007370bffcSMatthew Dillon if (__predict_false(error != 0 && error != EFBIG)) {
20017370bffcSMatthew Dillon device_printf(sc->sc_dev,
20027370bffcSMatthew Dillon "%s: bus_dmamap_load failed, error %d\n", __func__, error);
20037370bffcSMatthew Dillon m_freem(m1);
20047370bffcSMatthew Dillon
20057370bffcSMatthew Dillon /* Try to reload the old mbuf. */
20067370bffcSMatthew Dillon error = bus_dmamap_load(ring->data_dmat, data->map,
20077370bffcSMatthew Dillon mtod(data->m, void *), MJUMPAGESIZE, wpi_dma_map_addr,
20087370bffcSMatthew Dillon &paddr, BUS_DMA_NOWAIT);
20097370bffcSMatthew Dillon if (error != 0 && error != EFBIG) {
20107370bffcSMatthew Dillon panic("%s: could not load old RX mbuf", __func__);
20117370bffcSMatthew Dillon }
20127370bffcSMatthew Dillon /* Physical address may have changed. */
20137370bffcSMatthew Dillon ring->desc[ring->cur] = htole32(paddr);
20147370bffcSMatthew Dillon bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map,
20157370bffcSMatthew Dillon BUS_DMASYNC_PREWRITE);
20167370bffcSMatthew Dillon goto fail1;
20177370bffcSMatthew Dillon }
20187370bffcSMatthew Dillon
20197370bffcSMatthew Dillon m = data->m;
20207370bffcSMatthew Dillon data->m = m1;
20217370bffcSMatthew Dillon /* Update RX descriptor. */
20227370bffcSMatthew Dillon ring->desc[ring->cur] = htole32(paddr);
20237370bffcSMatthew Dillon bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
20247370bffcSMatthew Dillon BUS_DMASYNC_PREWRITE);
20257370bffcSMatthew Dillon
20267370bffcSMatthew Dillon /* Finalize mbuf. */
20277370bffcSMatthew Dillon m->m_data = (caddr_t)(head + 1);
20287370bffcSMatthew Dillon m->m_pkthdr.len = m->m_len = len;
20297370bffcSMatthew Dillon
20307370bffcSMatthew Dillon /* Grab a reference to the source node. */
20317370bffcSMatthew Dillon wh = mtod(m, struct ieee80211_frame *);
20327370bffcSMatthew Dillon
20337370bffcSMatthew Dillon if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
20347370bffcSMatthew Dillon (flags & WPI_RX_CIPHER_MASK) == WPI_RX_CIPHER_CCMP) {
20357370bffcSMatthew Dillon /* Check whether decryption was successful or not. */
20367370bffcSMatthew Dillon if ((flags & WPI_RX_DECRYPT_MASK) != WPI_RX_DECRYPT_OK) {
20377370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_RECV,
20387370bffcSMatthew Dillon "CCMP decryption failed 0x%x\n", flags);
20397370bffcSMatthew Dillon goto fail2;
20407370bffcSMatthew Dillon }
20417370bffcSMatthew Dillon m->m_flags |= M_WEP;
20427370bffcSMatthew Dillon }
20437370bffcSMatthew Dillon
20447370bffcSMatthew Dillon if (len >= sizeof(struct ieee80211_frame_min))
20457370bffcSMatthew Dillon ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
20467370bffcSMatthew Dillon else
20477370bffcSMatthew Dillon ni = NULL;
20487370bffcSMatthew Dillon
20497370bffcSMatthew Dillon sc->rx_tstamp = tail->tstamp;
20507370bffcSMatthew Dillon
20517370bffcSMatthew Dillon if (ieee80211_radiotap_active(ic)) {
20527370bffcSMatthew Dillon struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap;
20537370bffcSMatthew Dillon
20547370bffcSMatthew Dillon tap->wr_flags = 0;
20557370bffcSMatthew Dillon if (head->flags & htole16(WPI_STAT_FLAG_SHPREAMBLE))
20567370bffcSMatthew Dillon tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
20577370bffcSMatthew Dillon tap->wr_dbm_antsignal = (int8_t)(stat->rssi + WPI_RSSI_OFFSET);
20587370bffcSMatthew Dillon tap->wr_dbm_antnoise = WPI_RSSI_OFFSET;
20597370bffcSMatthew Dillon tap->wr_tsft = tail->tstamp;
20607370bffcSMatthew Dillon tap->wr_antenna = (le16toh(head->flags) >> 4) & 0xf;
20617370bffcSMatthew Dillon tap->wr_rate = plcp2rate(head->plcp);
20627370bffcSMatthew Dillon }
20637370bffcSMatthew Dillon
20647370bffcSMatthew Dillon WPI_UNLOCK(sc);
20657370bffcSMatthew Dillon
20667370bffcSMatthew Dillon /* Send the frame to the 802.11 layer. */
20677370bffcSMatthew Dillon if (ni != NULL) {
20687370bffcSMatthew Dillon (void)ieee80211_input(ni, m, stat->rssi, WPI_RSSI_OFFSET);
20697370bffcSMatthew Dillon /* Node is no longer needed. */
20707370bffcSMatthew Dillon ieee80211_free_node(ni);
20717370bffcSMatthew Dillon } else
20727370bffcSMatthew Dillon (void)ieee80211_input_all(ic, m, stat->rssi, WPI_RSSI_OFFSET);
20737370bffcSMatthew Dillon
20747370bffcSMatthew Dillon WPI_LOCK(sc);
20757370bffcSMatthew Dillon
20767370bffcSMatthew Dillon return;
20777370bffcSMatthew Dillon
20787370bffcSMatthew Dillon fail2: m_freem(m);
20797370bffcSMatthew Dillon
20807370bffcSMatthew Dillon #if defined(__DragonFly__)
20817370bffcSMatthew Dillon fail1: ; /* not implemented */
20827370bffcSMatthew Dillon #else
20837370bffcSMatthew Dillon fail1: counter_u64_add(ic->ic_ierrors, 1);
20847370bffcSMatthew Dillon #endif
20857370bffcSMatthew Dillon }
20867370bffcSMatthew Dillon
20877370bffcSMatthew Dillon static void
wpi_rx_statistics(struct wpi_softc * sc,struct wpi_rx_desc * desc,struct wpi_rx_data * data)20887370bffcSMatthew Dillon wpi_rx_statistics(struct wpi_softc *sc, struct wpi_rx_desc *desc,
20897370bffcSMatthew Dillon struct wpi_rx_data *data)
20907370bffcSMatthew Dillon {
20917370bffcSMatthew Dillon /* Ignore */
20927370bffcSMatthew Dillon }
20937370bffcSMatthew Dillon
20947370bffcSMatthew Dillon static void
wpi_tx_done(struct wpi_softc * sc,struct wpi_rx_desc * desc)20957370bffcSMatthew Dillon wpi_tx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc)
20967370bffcSMatthew Dillon {
20977370bffcSMatthew Dillon struct wpi_tx_ring *ring = &sc->txq[desc->qid & 0x3];
20987370bffcSMatthew Dillon struct wpi_tx_data *data = &ring->data[desc->idx];
20997370bffcSMatthew Dillon struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1);
21007370bffcSMatthew Dillon struct mbuf *m;
21017370bffcSMatthew Dillon struct ieee80211_node *ni;
21027370bffcSMatthew Dillon struct ieee80211vap *vap;
21037370bffcSMatthew Dillon uint32_t status = le32toh(stat->status);
21047370bffcSMatthew Dillon int ackfailcnt = stat->ackfailcnt / WPI_NTRIES_DEFAULT;
21057370bffcSMatthew Dillon
21067370bffcSMatthew Dillon KASSERT(data->ni != NULL, ("no node"));
21077370bffcSMatthew Dillon KASSERT(data->m != NULL, ("no mbuf"));
21087370bffcSMatthew Dillon
21097370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
21107370bffcSMatthew Dillon
21117370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_XMIT, "%s: "
21127370bffcSMatthew Dillon "qid %d idx %d retries %d btkillcnt %d rate %x duration %d "
21137370bffcSMatthew Dillon "status %x\n", __func__, desc->qid, desc->idx, stat->ackfailcnt,
21147370bffcSMatthew Dillon stat->btkillcnt, stat->rate, le32toh(stat->duration), status);
21157370bffcSMatthew Dillon
21167370bffcSMatthew Dillon /* Unmap and free mbuf. */
21177370bffcSMatthew Dillon bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE);
21187370bffcSMatthew Dillon bus_dmamap_unload(ring->data_dmat, data->map);
21197370bffcSMatthew Dillon m = data->m, data->m = NULL;
21207370bffcSMatthew Dillon ni = data->ni, data->ni = NULL;
21217370bffcSMatthew Dillon vap = ni->ni_vap;
21227370bffcSMatthew Dillon
21237370bffcSMatthew Dillon /*
21247370bffcSMatthew Dillon * Update rate control statistics for the node.
21257370bffcSMatthew Dillon */
21267370bffcSMatthew Dillon if (status & WPI_TX_STATUS_FAIL) {
21277370bffcSMatthew Dillon ieee80211_ratectl_tx_complete(vap, ni,
21287370bffcSMatthew Dillon IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
21297370bffcSMatthew Dillon } else
21307370bffcSMatthew Dillon ieee80211_ratectl_tx_complete(vap, ni,
21317370bffcSMatthew Dillon IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
21327370bffcSMatthew Dillon
21337370bffcSMatthew Dillon ieee80211_tx_complete(ni, m, (status & WPI_TX_STATUS_FAIL) != 0);
21347370bffcSMatthew Dillon
21357370bffcSMatthew Dillon WPI_TXQ_STATE_LOCK(sc);
21367370bffcSMatthew Dillon if (--ring->queued > 0)
21377370bffcSMatthew Dillon callout_reset(&sc->tx_timeout, 5*hz, wpi_tx_timeout, sc);
21387370bffcSMatthew Dillon else
21397370bffcSMatthew Dillon callout_stop(&sc->tx_timeout);
21407370bffcSMatthew Dillon WPI_TXQ_STATE_UNLOCK(sc);
21417370bffcSMatthew Dillon
21427370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
21437370bffcSMatthew Dillon }
21447370bffcSMatthew Dillon
21457370bffcSMatthew Dillon /*
21467370bffcSMatthew Dillon * Process a "command done" firmware notification. This is where we wakeup
21477370bffcSMatthew Dillon * processes waiting for a synchronous command completion.
21487370bffcSMatthew Dillon */
21497370bffcSMatthew Dillon static void
wpi_cmd_done(struct wpi_softc * sc,struct wpi_rx_desc * desc)21507370bffcSMatthew Dillon wpi_cmd_done(struct wpi_softc *sc, struct wpi_rx_desc *desc)
21517370bffcSMatthew Dillon {
21527370bffcSMatthew Dillon struct wpi_tx_ring *ring = &sc->txq[WPI_CMD_QUEUE_NUM];
21534db7dd1bSJoe Talbott struct wpi_tx_data *data;
21544db7dd1bSJoe Talbott struct wpi_tx_cmd *cmd;
21557370bffcSMatthew Dillon
21567370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_CMD, "cmd notification qid %x idx %d flags %x "
21577370bffcSMatthew Dillon "type %s len %d\n", desc->qid, desc->idx,
21587370bffcSMatthew Dillon desc->flags, wpi_cmd_str(desc->type),
21597370bffcSMatthew Dillon le32toh(desc->len));
21607370bffcSMatthew Dillon
21617370bffcSMatthew Dillon if ((desc->qid & WPI_RX_DESC_QID_MSK) != WPI_CMD_QUEUE_NUM)
21627370bffcSMatthew Dillon return; /* Not a command ack. */
21637370bffcSMatthew Dillon
21647370bffcSMatthew Dillon KASSERT(ring->queued == 0, ("ring->queued must be 0"));
21657370bffcSMatthew Dillon
21667370bffcSMatthew Dillon data = &ring->data[desc->idx];
21677370bffcSMatthew Dillon cmd = &ring->cmd[desc->idx];
21687370bffcSMatthew Dillon
21697370bffcSMatthew Dillon /* If the command was mapped in an mbuf, free it. */
21707370bffcSMatthew Dillon if (data->m != NULL) {
21717370bffcSMatthew Dillon bus_dmamap_sync(ring->data_dmat, data->map,
21727370bffcSMatthew Dillon BUS_DMASYNC_POSTWRITE);
21737370bffcSMatthew Dillon bus_dmamap_unload(ring->data_dmat, data->map);
21747370bffcSMatthew Dillon m_freem(data->m);
21757370bffcSMatthew Dillon data->m = NULL;
21767370bffcSMatthew Dillon }
21777370bffcSMatthew Dillon
21787370bffcSMatthew Dillon wakeup(cmd);
21797370bffcSMatthew Dillon
21807370bffcSMatthew Dillon if (desc->type == WPI_CMD_SET_POWER_MODE) {
21817370bffcSMatthew Dillon struct wpi_pmgt_cmd *pcmd = (struct wpi_pmgt_cmd *)cmd->data;
21827370bffcSMatthew Dillon
21837370bffcSMatthew Dillon bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
21847370bffcSMatthew Dillon BUS_DMASYNC_POSTREAD);
21857370bffcSMatthew Dillon
21867370bffcSMatthew Dillon WPI_TXQ_LOCK(sc);
21877370bffcSMatthew Dillon if (le16toh(pcmd->flags) & WPI_PS_ALLOW_SLEEP) {
21887370bffcSMatthew Dillon sc->sc_update_rx_ring = wpi_update_rx_ring_ps;
21897370bffcSMatthew Dillon sc->sc_update_tx_ring = wpi_update_tx_ring_ps;
21907370bffcSMatthew Dillon } else {
21917370bffcSMatthew Dillon sc->sc_update_rx_ring = wpi_update_rx_ring;
21927370bffcSMatthew Dillon sc->sc_update_tx_ring = wpi_update_tx_ring;
21937370bffcSMatthew Dillon }
21947370bffcSMatthew Dillon WPI_TXQ_UNLOCK(sc);
21957370bffcSMatthew Dillon }
21967370bffcSMatthew Dillon }
21977370bffcSMatthew Dillon
21987370bffcSMatthew Dillon static void
wpi_notif_intr(struct wpi_softc * sc)21997370bffcSMatthew Dillon wpi_notif_intr(struct wpi_softc *sc)
22007370bffcSMatthew Dillon {
22017370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
22027370bffcSMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
22037370bffcSMatthew Dillon uint32_t hw;
22047370bffcSMatthew Dillon
22057370bffcSMatthew Dillon bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map,
22067370bffcSMatthew Dillon BUS_DMASYNC_POSTREAD);
22077370bffcSMatthew Dillon
22087370bffcSMatthew Dillon hw = le32toh(sc->shared->next) & 0xfff;
22097370bffcSMatthew Dillon hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1;
22107370bffcSMatthew Dillon
22117370bffcSMatthew Dillon while (sc->rxq.cur != hw) {
22127370bffcSMatthew Dillon sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT;
22137370bffcSMatthew Dillon
22147370bffcSMatthew Dillon struct wpi_rx_data *data = &sc->rxq.data[sc->rxq.cur];
22157370bffcSMatthew Dillon struct wpi_rx_desc *desc;
22167370bffcSMatthew Dillon
22177370bffcSMatthew Dillon bus_dmamap_sync(sc->rxq.data_dmat, data->map,
22187370bffcSMatthew Dillon BUS_DMASYNC_POSTREAD);
22197370bffcSMatthew Dillon desc = mtod(data->m, struct wpi_rx_desc *);
22207370bffcSMatthew Dillon
22217370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_NOTIFY,
22227370bffcSMatthew Dillon "%s: cur=%d; qid %x idx %d flags %x type %d(%s) len %d\n",
22237370bffcSMatthew Dillon __func__, sc->rxq.cur, desc->qid, desc->idx, desc->flags,
22247370bffcSMatthew Dillon desc->type, wpi_cmd_str(desc->type), le32toh(desc->len));
22257370bffcSMatthew Dillon
22267370bffcSMatthew Dillon if (!(desc->qid & WPI_UNSOLICITED_RX_NOTIF)) {
22277370bffcSMatthew Dillon /* Reply to a command. */
22287370bffcSMatthew Dillon wpi_cmd_done(sc, desc);
22297370bffcSMatthew Dillon }
22307370bffcSMatthew Dillon
22317370bffcSMatthew Dillon switch (desc->type) {
22327370bffcSMatthew Dillon case WPI_RX_DONE:
22337370bffcSMatthew Dillon /* An 802.11 frame has been received. */
22347370bffcSMatthew Dillon wpi_rx_done(sc, desc, data);
22357370bffcSMatthew Dillon
22367370bffcSMatthew Dillon if (__predict_false(sc->sc_running == 0)) {
22377370bffcSMatthew Dillon /* wpi_stop() was called. */
22387370bffcSMatthew Dillon return;
22397370bffcSMatthew Dillon }
22407370bffcSMatthew Dillon
22417370bffcSMatthew Dillon break;
22427370bffcSMatthew Dillon
22437370bffcSMatthew Dillon case WPI_TX_DONE:
22447370bffcSMatthew Dillon /* An 802.11 frame has been transmitted. */
22457370bffcSMatthew Dillon wpi_tx_done(sc, desc);
22467370bffcSMatthew Dillon break;
22477370bffcSMatthew Dillon
22487370bffcSMatthew Dillon case WPI_RX_STATISTICS:
22497370bffcSMatthew Dillon case WPI_BEACON_STATISTICS:
22507370bffcSMatthew Dillon wpi_rx_statistics(sc, desc, data);
22517370bffcSMatthew Dillon break;
22527370bffcSMatthew Dillon
22537370bffcSMatthew Dillon case WPI_BEACON_MISSED:
22547370bffcSMatthew Dillon {
22557370bffcSMatthew Dillon struct wpi_beacon_missed *miss =
22567370bffcSMatthew Dillon (struct wpi_beacon_missed *)(desc + 1);
22577370bffcSMatthew Dillon uint32_t expected, misses, received, threshold;
22587370bffcSMatthew Dillon
22597370bffcSMatthew Dillon bus_dmamap_sync(sc->rxq.data_dmat, data->map,
22607370bffcSMatthew Dillon BUS_DMASYNC_POSTREAD);
22617370bffcSMatthew Dillon
22627370bffcSMatthew Dillon misses = le32toh(miss->consecutive);
22637370bffcSMatthew Dillon expected = le32toh(miss->expected);
22647370bffcSMatthew Dillon received = le32toh(miss->received);
22657370bffcSMatthew Dillon threshold = MAX(2, vap->iv_bmissthreshold);
22667370bffcSMatthew Dillon
22677370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_BMISS,
22687370bffcSMatthew Dillon "%s: beacons missed %u(%u) (received %u/%u)\n",
22697370bffcSMatthew Dillon __func__, misses, le32toh(miss->total), received,
22707370bffcSMatthew Dillon expected);
22717370bffcSMatthew Dillon
22727370bffcSMatthew Dillon if (misses >= threshold ||
22737370bffcSMatthew Dillon (received == 0 && expected >= threshold)) {
22747370bffcSMatthew Dillon WPI_RXON_LOCK(sc);
22757370bffcSMatthew Dillon if (callout_pending(&sc->scan_timeout)) {
22767370bffcSMatthew Dillon wpi_cmd(sc, WPI_CMD_SCAN_ABORT, NULL,
22777370bffcSMatthew Dillon 0, 1);
22787370bffcSMatthew Dillon }
22797370bffcSMatthew Dillon WPI_RXON_UNLOCK(sc);
22807370bffcSMatthew Dillon if (vap->iv_state == IEEE80211_S_RUN &&
22817370bffcSMatthew Dillon (ic->ic_flags & IEEE80211_F_SCAN) == 0)
22827370bffcSMatthew Dillon ieee80211_beacon_miss(ic);
22837370bffcSMatthew Dillon }
22847370bffcSMatthew Dillon
22857370bffcSMatthew Dillon break;
22867370bffcSMatthew Dillon }
22877370bffcSMatthew Dillon #ifdef WPI_DEBUG
22887370bffcSMatthew Dillon case WPI_BEACON_SENT:
22897370bffcSMatthew Dillon {
22907370bffcSMatthew Dillon struct wpi_tx_stat *stat =
22917370bffcSMatthew Dillon (struct wpi_tx_stat *)(desc + 1);
22927370bffcSMatthew Dillon uint64_t *tsf = (uint64_t *)(stat + 1);
22937370bffcSMatthew Dillon uint32_t *mode = (uint32_t *)(tsf + 1);
22947370bffcSMatthew Dillon
22957370bffcSMatthew Dillon bus_dmamap_sync(sc->rxq.data_dmat, data->map,
22967370bffcSMatthew Dillon BUS_DMASYNC_POSTREAD);
22977370bffcSMatthew Dillon
22987370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_BEACON,
22997370bffcSMatthew Dillon "beacon sent: rts %u, ack %u, btkill %u, rate %u, "
23007370bffcSMatthew Dillon "duration %u, status %x, tsf %ju, mode %x\n",
23017370bffcSMatthew Dillon stat->rtsfailcnt, stat->ackfailcnt,
23027370bffcSMatthew Dillon stat->btkillcnt, stat->rate, le32toh(stat->duration),
23037370bffcSMatthew Dillon le32toh(stat->status), le64toh(*tsf),
23047370bffcSMatthew Dillon le32toh(*mode));
23057370bffcSMatthew Dillon
23067370bffcSMatthew Dillon break;
23077370bffcSMatthew Dillon }
23087370bffcSMatthew Dillon #endif
23097370bffcSMatthew Dillon case WPI_UC_READY:
23107370bffcSMatthew Dillon {
23117370bffcSMatthew Dillon struct wpi_ucode_info *uc =
23127370bffcSMatthew Dillon (struct wpi_ucode_info *)(desc + 1);
23137370bffcSMatthew Dillon
23147370bffcSMatthew Dillon /* The microcontroller is ready. */
23157370bffcSMatthew Dillon bus_dmamap_sync(sc->rxq.data_dmat, data->map,
23167370bffcSMatthew Dillon BUS_DMASYNC_POSTREAD);
23177370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_RESET,
23187370bffcSMatthew Dillon "microcode alive notification version=%d.%d "
23197370bffcSMatthew Dillon "subtype=%x alive=%x\n", uc->major, uc->minor,
23207370bffcSMatthew Dillon uc->subtype, le32toh(uc->valid));
23217370bffcSMatthew Dillon
23227370bffcSMatthew Dillon if (le32toh(uc->valid) != 1) {
23237370bffcSMatthew Dillon device_printf(sc->sc_dev,
23247370bffcSMatthew Dillon "microcontroller initialization failed\n");
23257370bffcSMatthew Dillon wpi_stop_locked(sc);
23267370bffcSMatthew Dillon return;
23277370bffcSMatthew Dillon }
23287370bffcSMatthew Dillon /* Save the address of the error log in SRAM. */
23297370bffcSMatthew Dillon sc->errptr = le32toh(uc->errptr);
23307370bffcSMatthew Dillon break;
23317370bffcSMatthew Dillon }
23327370bffcSMatthew Dillon case WPI_STATE_CHANGED:
23337370bffcSMatthew Dillon {
23347370bffcSMatthew Dillon bus_dmamap_sync(sc->rxq.data_dmat, data->map,
23357370bffcSMatthew Dillon BUS_DMASYNC_POSTREAD);
23367370bffcSMatthew Dillon
23377370bffcSMatthew Dillon uint32_t *status = (uint32_t *)(desc + 1);
23387370bffcSMatthew Dillon
23397370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_STATE, "state changed to %x\n",
23407370bffcSMatthew Dillon le32toh(*status));
23417370bffcSMatthew Dillon
23427370bffcSMatthew Dillon if (le32toh(*status) & 1) {
23437370bffcSMatthew Dillon WPI_NT_LOCK(sc);
23447370bffcSMatthew Dillon wpi_clear_node_table(sc);
23457370bffcSMatthew Dillon WPI_NT_UNLOCK(sc);
23467370bffcSMatthew Dillon ieee80211_runtask(ic,
23477370bffcSMatthew Dillon &sc->sc_radiooff_task);
23487370bffcSMatthew Dillon return;
23497370bffcSMatthew Dillon }
23507370bffcSMatthew Dillon break;
23517370bffcSMatthew Dillon }
23527370bffcSMatthew Dillon #ifdef WPI_DEBUG
23537370bffcSMatthew Dillon case WPI_START_SCAN:
23547370bffcSMatthew Dillon {
23557370bffcSMatthew Dillon bus_dmamap_sync(sc->rxq.data_dmat, data->map,
23567370bffcSMatthew Dillon BUS_DMASYNC_POSTREAD);
23577370bffcSMatthew Dillon
23587370bffcSMatthew Dillon struct wpi_start_scan *scan =
23597370bffcSMatthew Dillon (struct wpi_start_scan *)(desc + 1);
23607370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_SCAN,
23617370bffcSMatthew Dillon "%s: scanning channel %d status %x\n",
23627370bffcSMatthew Dillon __func__, scan->chan, le32toh(scan->status));
23637370bffcSMatthew Dillon
23647370bffcSMatthew Dillon break;
23657370bffcSMatthew Dillon }
23667370bffcSMatthew Dillon #endif
23677370bffcSMatthew Dillon case WPI_STOP_SCAN:
23687370bffcSMatthew Dillon {
23697370bffcSMatthew Dillon bus_dmamap_sync(sc->rxq.data_dmat, data->map,
23707370bffcSMatthew Dillon BUS_DMASYNC_POSTREAD);
23717370bffcSMatthew Dillon
23727370bffcSMatthew Dillon struct wpi_stop_scan *scan =
23737370bffcSMatthew Dillon (struct wpi_stop_scan *)(desc + 1);
23747370bffcSMatthew Dillon
23757370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_SCAN,
23767370bffcSMatthew Dillon "scan finished nchan=%d status=%d chan=%d\n",
23777370bffcSMatthew Dillon scan->nchan, scan->status, scan->chan);
23787370bffcSMatthew Dillon
23797370bffcSMatthew Dillon WPI_RXON_LOCK(sc);
23807370bffcSMatthew Dillon callout_stop(&sc->scan_timeout);
23817370bffcSMatthew Dillon WPI_RXON_UNLOCK(sc);
23827370bffcSMatthew Dillon if (scan->status == WPI_SCAN_ABORTED)
23837370bffcSMatthew Dillon ieee80211_cancel_scan(vap);
23847370bffcSMatthew Dillon else
23857370bffcSMatthew Dillon ieee80211_scan_next(vap);
23867370bffcSMatthew Dillon break;
23877370bffcSMatthew Dillon }
23887370bffcSMatthew Dillon }
23897370bffcSMatthew Dillon
23907370bffcSMatthew Dillon if (sc->rxq.cur % 8 == 0) {
23917370bffcSMatthew Dillon /* Tell the firmware what we have processed. */
23927370bffcSMatthew Dillon sc->sc_update_rx_ring(sc);
23937370bffcSMatthew Dillon }
23947370bffcSMatthew Dillon }
23957370bffcSMatthew Dillon }
23967370bffcSMatthew Dillon
23977370bffcSMatthew Dillon /*
23987370bffcSMatthew Dillon * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up
23997370bffcSMatthew Dillon * from power-down sleep mode.
24007370bffcSMatthew Dillon */
24017370bffcSMatthew Dillon static void
wpi_wakeup_intr(struct wpi_softc * sc)24027370bffcSMatthew Dillon wpi_wakeup_intr(struct wpi_softc *sc)
24037370bffcSMatthew Dillon {
24047370bffcSMatthew Dillon int qid;
24057370bffcSMatthew Dillon
24067370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_PWRSAVE,
24077370bffcSMatthew Dillon "%s: ucode wakeup from power-down sleep\n", __func__);
24087370bffcSMatthew Dillon
24097370bffcSMatthew Dillon /* Wakeup RX and TX rings. */
24107370bffcSMatthew Dillon if (sc->rxq.update) {
24117370bffcSMatthew Dillon sc->rxq.update = 0;
24127370bffcSMatthew Dillon wpi_update_rx_ring(sc);
24137370bffcSMatthew Dillon }
24147370bffcSMatthew Dillon WPI_TXQ_LOCK(sc);
24157370bffcSMatthew Dillon for (qid = 0; qid < WPI_DRV_NTXQUEUES; qid++) {
24167370bffcSMatthew Dillon struct wpi_tx_ring *ring = &sc->txq[qid];
24177370bffcSMatthew Dillon
24187370bffcSMatthew Dillon if (ring->update) {
24197370bffcSMatthew Dillon ring->update = 0;
24207370bffcSMatthew Dillon wpi_update_tx_ring(sc, ring);
24217370bffcSMatthew Dillon }
24227370bffcSMatthew Dillon }
24237370bffcSMatthew Dillon WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ);
24247370bffcSMatthew Dillon WPI_TXQ_UNLOCK(sc);
24257370bffcSMatthew Dillon }
24267370bffcSMatthew Dillon
24277370bffcSMatthew Dillon /*
24287370bffcSMatthew Dillon * This function prints firmware registers
24297370bffcSMatthew Dillon */
24307370bffcSMatthew Dillon #ifdef WPI_DEBUG
24317370bffcSMatthew Dillon static void
wpi_debug_registers(struct wpi_softc * sc)24327370bffcSMatthew Dillon wpi_debug_registers(struct wpi_softc *sc)
24337370bffcSMatthew Dillon {
24347370bffcSMatthew Dillon size_t i;
24357370bffcSMatthew Dillon static const uint32_t csr_tbl[] = {
24367370bffcSMatthew Dillon WPI_HW_IF_CONFIG,
24377370bffcSMatthew Dillon WPI_INT,
24387370bffcSMatthew Dillon WPI_INT_MASK,
24397370bffcSMatthew Dillon WPI_FH_INT,
24407370bffcSMatthew Dillon WPI_GPIO_IN,
24417370bffcSMatthew Dillon WPI_RESET,
24427370bffcSMatthew Dillon WPI_GP_CNTRL,
24437370bffcSMatthew Dillon WPI_EEPROM,
24447370bffcSMatthew Dillon WPI_EEPROM_GP,
24457370bffcSMatthew Dillon WPI_GIO,
24467370bffcSMatthew Dillon WPI_UCODE_GP1,
24477370bffcSMatthew Dillon WPI_UCODE_GP2,
24487370bffcSMatthew Dillon WPI_GIO_CHICKEN,
24497370bffcSMatthew Dillon WPI_ANA_PLL,
24507370bffcSMatthew Dillon WPI_DBG_HPET_MEM,
24517370bffcSMatthew Dillon };
24527370bffcSMatthew Dillon static const uint32_t prph_tbl[] = {
24537370bffcSMatthew Dillon WPI_APMG_CLK_CTRL,
24547370bffcSMatthew Dillon WPI_APMG_PS,
24557370bffcSMatthew Dillon WPI_APMG_PCI_STT,
24567370bffcSMatthew Dillon WPI_APMG_RFKILL,
24577370bffcSMatthew Dillon };
24587370bffcSMatthew Dillon
24597370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_REGISTER,"%s","\n");
24607370bffcSMatthew Dillon
24617370bffcSMatthew Dillon for (i = 0; i < nitems(csr_tbl); i++) {
24627370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_REGISTER, " %-18s: 0x%08x ",
24637370bffcSMatthew Dillon wpi_get_csr_string(csr_tbl[i]), WPI_READ(sc, csr_tbl[i]));
24647370bffcSMatthew Dillon
24657370bffcSMatthew Dillon if ((i + 1) % 2 == 0)
24667370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_REGISTER, "\n");
24677370bffcSMatthew Dillon }
24687370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_REGISTER, "\n\n");
24697370bffcSMatthew Dillon
24707370bffcSMatthew Dillon if (wpi_nic_lock(sc) == 0) {
24717370bffcSMatthew Dillon for (i = 0; i < nitems(prph_tbl); i++) {
24727370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_REGISTER, " %-18s: 0x%08x ",
24737370bffcSMatthew Dillon wpi_get_prph_string(prph_tbl[i]),
24747370bffcSMatthew Dillon wpi_prph_read(sc, prph_tbl[i]));
24757370bffcSMatthew Dillon
24767370bffcSMatthew Dillon if ((i + 1) % 2 == 0)
24777370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_REGISTER, "\n");
24787370bffcSMatthew Dillon }
24797370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_REGISTER, "\n");
24807370bffcSMatthew Dillon wpi_nic_unlock(sc);
24817370bffcSMatthew Dillon } else {
24827370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_REGISTER,
24837370bffcSMatthew Dillon "Cannot access internal registers.\n");
24847370bffcSMatthew Dillon }
24857370bffcSMatthew Dillon }
24867370bffcSMatthew Dillon #endif
24877370bffcSMatthew Dillon
24887370bffcSMatthew Dillon /*
24897370bffcSMatthew Dillon * Dump the error log of the firmware when a firmware panic occurs. Although
24907370bffcSMatthew Dillon * we can't debug the firmware because it is neither open source nor free, it
24917370bffcSMatthew Dillon * can help us to identify certain classes of problems.
24927370bffcSMatthew Dillon */
24937370bffcSMatthew Dillon static void
wpi_fatal_intr(struct wpi_softc * sc)24947370bffcSMatthew Dillon wpi_fatal_intr(struct wpi_softc *sc)
24957370bffcSMatthew Dillon {
24967370bffcSMatthew Dillon struct wpi_fw_dump dump;
24977370bffcSMatthew Dillon uint32_t i, offset, count;
24987370bffcSMatthew Dillon
24997370bffcSMatthew Dillon /* Check that the error log address is valid. */
25007370bffcSMatthew Dillon if (sc->errptr < WPI_FW_DATA_BASE ||
25017370bffcSMatthew Dillon sc->errptr + sizeof (dump) >
25027370bffcSMatthew Dillon WPI_FW_DATA_BASE + WPI_FW_DATA_MAXSZ) {
25037370bffcSMatthew Dillon kprintf("%s: bad firmware error log address 0x%08x\n", __func__,
25047370bffcSMatthew Dillon sc->errptr);
25057370bffcSMatthew Dillon return;
25067370bffcSMatthew Dillon }
25077370bffcSMatthew Dillon if (wpi_nic_lock(sc) != 0) {
25087370bffcSMatthew Dillon kprintf("%s: could not read firmware error log\n", __func__);
25097370bffcSMatthew Dillon return;
25107370bffcSMatthew Dillon }
25117370bffcSMatthew Dillon /* Read number of entries in the log. */
25127370bffcSMatthew Dillon count = wpi_mem_read(sc, sc->errptr);
25137370bffcSMatthew Dillon if (count == 0 || count * sizeof (dump) > WPI_FW_DATA_MAXSZ) {
25147370bffcSMatthew Dillon kprintf("%s: invalid count field (count = %u)\n", __func__,
25157370bffcSMatthew Dillon count);
25167370bffcSMatthew Dillon wpi_nic_unlock(sc);
25177370bffcSMatthew Dillon return;
25187370bffcSMatthew Dillon }
25197370bffcSMatthew Dillon /* Skip "count" field. */
25207370bffcSMatthew Dillon offset = sc->errptr + sizeof (uint32_t);
25217370bffcSMatthew Dillon kprintf("firmware error log (count = %u):\n", count);
25227370bffcSMatthew Dillon for (i = 0; i < count; i++) {
25237370bffcSMatthew Dillon wpi_mem_read_region_4(sc, offset, (uint32_t *)&dump,
25247370bffcSMatthew Dillon sizeof (dump) / sizeof (uint32_t));
25257370bffcSMatthew Dillon
25267370bffcSMatthew Dillon kprintf(" error type = \"%s\" (0x%08X)\n",
25277370bffcSMatthew Dillon (dump.desc < nitems(wpi_fw_errmsg)) ?
25287370bffcSMatthew Dillon wpi_fw_errmsg[dump.desc] : "UNKNOWN",
25297370bffcSMatthew Dillon dump.desc);
25307370bffcSMatthew Dillon kprintf(" error data = 0x%08X\n",
25317370bffcSMatthew Dillon dump.data);
25327370bffcSMatthew Dillon kprintf(" branch link = 0x%08X%08X\n",
25337370bffcSMatthew Dillon dump.blink[0], dump.blink[1]);
25347370bffcSMatthew Dillon kprintf(" interrupt link = 0x%08X%08X\n",
25357370bffcSMatthew Dillon dump.ilink[0], dump.ilink[1]);
25367370bffcSMatthew Dillon kprintf(" time = %u\n", dump.time);
25377370bffcSMatthew Dillon
25387370bffcSMatthew Dillon offset += sizeof (dump);
25397370bffcSMatthew Dillon }
25407370bffcSMatthew Dillon wpi_nic_unlock(sc);
25417370bffcSMatthew Dillon /* Dump driver status (TX and RX rings) while we're here. */
25427370bffcSMatthew Dillon kprintf("driver status:\n");
25437370bffcSMatthew Dillon WPI_TXQ_LOCK(sc);
25447370bffcSMatthew Dillon for (i = 0; i < WPI_DRV_NTXQUEUES; i++) {
25457370bffcSMatthew Dillon struct wpi_tx_ring *ring = &sc->txq[i];
25467370bffcSMatthew Dillon kprintf(" tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n",
25477370bffcSMatthew Dillon i, ring->qid, ring->cur, ring->queued);
25487370bffcSMatthew Dillon }
25497370bffcSMatthew Dillon WPI_TXQ_UNLOCK(sc);
25507370bffcSMatthew Dillon kprintf(" rx ring: cur=%d\n", sc->rxq.cur);
25517370bffcSMatthew Dillon }
25527370bffcSMatthew Dillon
25537370bffcSMatthew Dillon static void
wpi_intr(void * arg)25547370bffcSMatthew Dillon wpi_intr(void *arg)
25557370bffcSMatthew Dillon {
25567370bffcSMatthew Dillon struct wpi_softc *sc = arg;
25577370bffcSMatthew Dillon uint32_t r1, r2;
25587370bffcSMatthew Dillon
25597370bffcSMatthew Dillon WPI_LOCK(sc);
25607370bffcSMatthew Dillon
25617370bffcSMatthew Dillon /* Disable interrupts. */
25627370bffcSMatthew Dillon WPI_WRITE(sc, WPI_INT_MASK, 0);
25637370bffcSMatthew Dillon
25647370bffcSMatthew Dillon r1 = WPI_READ(sc, WPI_INT);
25657370bffcSMatthew Dillon
25667370bffcSMatthew Dillon if (__predict_false(r1 == 0xffffffff ||
25677370bffcSMatthew Dillon (r1 & 0xfffffff0) == 0xa5a5a5a0))
25687370bffcSMatthew Dillon goto end; /* Hardware gone! */
25697370bffcSMatthew Dillon
25707370bffcSMatthew Dillon r2 = WPI_READ(sc, WPI_FH_INT);
25717370bffcSMatthew Dillon
25727370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_INTR, "%s: reg1=0x%08x reg2=0x%08x\n", __func__,
25737370bffcSMatthew Dillon r1, r2);
25747370bffcSMatthew Dillon
25757370bffcSMatthew Dillon if (r1 == 0 && r2 == 0)
25767370bffcSMatthew Dillon goto done; /* Interrupt not for us. */
25777370bffcSMatthew Dillon
25787370bffcSMatthew Dillon /* Acknowledge interrupts. */
25797370bffcSMatthew Dillon WPI_WRITE(sc, WPI_INT, r1);
25807370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_INT, r2);
25817370bffcSMatthew Dillon
25827370bffcSMatthew Dillon if (__predict_false(r1 & (WPI_INT_SW_ERR | WPI_INT_HW_ERR))) {
25837370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
25847370bffcSMatthew Dillon
25857370bffcSMatthew Dillon device_printf(sc->sc_dev, "fatal firmware error\n");
25867370bffcSMatthew Dillon #ifdef WPI_DEBUG
25877370bffcSMatthew Dillon wpi_debug_registers(sc);
25887370bffcSMatthew Dillon #endif
25897370bffcSMatthew Dillon wpi_fatal_intr(sc);
25907370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_HW,
25917370bffcSMatthew Dillon "(%s)\n", (r1 & WPI_INT_SW_ERR) ? "(Software Error)" :
25927370bffcSMatthew Dillon "(Hardware Error)");
25937370bffcSMatthew Dillon ieee80211_restart_all(ic);
25947370bffcSMatthew Dillon goto end;
25957370bffcSMatthew Dillon }
25967370bffcSMatthew Dillon
25977370bffcSMatthew Dillon if ((r1 & (WPI_INT_FH_RX | WPI_INT_SW_RX)) ||
25987370bffcSMatthew Dillon (r2 & WPI_FH_INT_RX))
25997370bffcSMatthew Dillon wpi_notif_intr(sc);
26007370bffcSMatthew Dillon
26017370bffcSMatthew Dillon if (r1 & WPI_INT_ALIVE)
26027370bffcSMatthew Dillon wakeup(sc); /* Firmware is alive. */
26037370bffcSMatthew Dillon
26047370bffcSMatthew Dillon if (r1 & WPI_INT_WAKEUP)
26057370bffcSMatthew Dillon wpi_wakeup_intr(sc);
26067370bffcSMatthew Dillon
26077370bffcSMatthew Dillon done:
26087370bffcSMatthew Dillon /* Re-enable interrupts. */
26097370bffcSMatthew Dillon if (__predict_true(sc->sc_running))
26107370bffcSMatthew Dillon WPI_WRITE(sc, WPI_INT_MASK, WPI_INT_MASK_DEF);
26117370bffcSMatthew Dillon
26127370bffcSMatthew Dillon end: WPI_UNLOCK(sc);
26137370bffcSMatthew Dillon }
26147370bffcSMatthew Dillon
26157370bffcSMatthew Dillon static void
wpi_free_txfrags(struct wpi_softc * sc,uint16_t ac)26167370bffcSMatthew Dillon wpi_free_txfrags(struct wpi_softc *sc, uint16_t ac)
26177370bffcSMatthew Dillon {
26187370bffcSMatthew Dillon struct wpi_tx_ring *ring;
26197370bffcSMatthew Dillon struct wpi_tx_data *data;
26207370bffcSMatthew Dillon uint8_t cur;
26217370bffcSMatthew Dillon
26227370bffcSMatthew Dillon WPI_TXQ_LOCK(sc);
26237370bffcSMatthew Dillon ring = &sc->txq[ac];
26247370bffcSMatthew Dillon
26257370bffcSMatthew Dillon while (ring->pending != 0) {
26267370bffcSMatthew Dillon ring->pending--;
26277370bffcSMatthew Dillon cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT;
26287370bffcSMatthew Dillon data = &ring->data[cur];
26297370bffcSMatthew Dillon
26307370bffcSMatthew Dillon bus_dmamap_sync(ring->data_dmat, data->map,
26317370bffcSMatthew Dillon BUS_DMASYNC_POSTWRITE);
26327370bffcSMatthew Dillon bus_dmamap_unload(ring->data_dmat, data->map);
26337370bffcSMatthew Dillon m_freem(data->m);
26347370bffcSMatthew Dillon data->m = NULL;
26357370bffcSMatthew Dillon
26367370bffcSMatthew Dillon ieee80211_node_decref(data->ni);
26377370bffcSMatthew Dillon data->ni = NULL;
26387370bffcSMatthew Dillon }
26397370bffcSMatthew Dillon
26407370bffcSMatthew Dillon WPI_TXQ_UNLOCK(sc);
26417370bffcSMatthew Dillon }
26427370bffcSMatthew Dillon
26437370bffcSMatthew Dillon static int
wpi_cmd2(struct wpi_softc * sc,struct wpi_buf * buf)26447370bffcSMatthew Dillon wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf)
26457370bffcSMatthew Dillon {
26464db7dd1bSJoe Talbott struct ieee80211_frame *wh;
26477370bffcSMatthew Dillon struct wpi_tx_cmd *cmd;
26487370bffcSMatthew Dillon struct wpi_tx_data *data;
26497370bffcSMatthew Dillon struct wpi_tx_desc *desc;
26507370bffcSMatthew Dillon struct wpi_tx_ring *ring;
26517370bffcSMatthew Dillon struct mbuf *m1;
26527370bffcSMatthew Dillon bus_dma_segment_t *seg, segs[WPI_MAX_SCATTER];
26537370bffcSMatthew Dillon uint8_t cur, pad;
26547370bffcSMatthew Dillon uint16_t hdrlen;
26557370bffcSMatthew Dillon int error, i, nsegs, totlen, frag;
26564db7dd1bSJoe Talbott
26577370bffcSMatthew Dillon WPI_TXQ_LOCK(sc);
26584db7dd1bSJoe Talbott
26597370bffcSMatthew Dillon KASSERT(buf->size <= sizeof(buf->data), ("buffer overflow"));
26604db7dd1bSJoe Talbott
26617370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
26624db7dd1bSJoe Talbott
26637370bffcSMatthew Dillon if (__predict_false(sc->sc_running == 0)) {
26647370bffcSMatthew Dillon /* wpi_stop() was called */
26657370bffcSMatthew Dillon error = ENETDOWN;
26667370bffcSMatthew Dillon goto end;
26674db7dd1bSJoe Talbott }
26684db7dd1bSJoe Talbott
26697370bffcSMatthew Dillon wh = mtod(buf->m, struct ieee80211_frame *);
26707370bffcSMatthew Dillon hdrlen = ieee80211_anyhdrsize(wh);
26717370bffcSMatthew Dillon totlen = buf->m->m_pkthdr.len;
26727370bffcSMatthew Dillon frag = ((buf->m->m_flags & (M_FRAG | M_LASTFRAG)) == M_FRAG);
26737370bffcSMatthew Dillon
26747370bffcSMatthew Dillon if (__predict_false(totlen < sizeof(struct ieee80211_frame_min))) {
26757370bffcSMatthew Dillon error = EINVAL;
26767370bffcSMatthew Dillon goto end;
26777370bffcSMatthew Dillon }
26787370bffcSMatthew Dillon
26797370bffcSMatthew Dillon if (hdrlen & 3) {
26807370bffcSMatthew Dillon /* First segment length must be a multiple of 4. */
26817370bffcSMatthew Dillon pad = 4 - (hdrlen & 3);
26827370bffcSMatthew Dillon } else
26837370bffcSMatthew Dillon pad = 0;
26847370bffcSMatthew Dillon
26857370bffcSMatthew Dillon ring = &sc->txq[buf->ac];
26867370bffcSMatthew Dillon cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT;
26877370bffcSMatthew Dillon desc = &ring->desc[cur];
26887370bffcSMatthew Dillon data = &ring->data[cur];
26897370bffcSMatthew Dillon
26907370bffcSMatthew Dillon /* Prepare TX firmware command. */
26917370bffcSMatthew Dillon cmd = &ring->cmd[cur];
26927370bffcSMatthew Dillon cmd->code = buf->code;
26934db7dd1bSJoe Talbott cmd->flags = 0;
26944db7dd1bSJoe Talbott cmd->qid = ring->qid;
26957370bffcSMatthew Dillon cmd->idx = cur;
26964db7dd1bSJoe Talbott
26977370bffcSMatthew Dillon memcpy(cmd->data, buf->data, buf->size);
26984db7dd1bSJoe Talbott
26997370bffcSMatthew Dillon /* Save and trim IEEE802.11 header. */
27007370bffcSMatthew Dillon memcpy((uint8_t *)(cmd->data + buf->size), wh, hdrlen);
27017370bffcSMatthew Dillon m_adj(buf->m, hdrlen);
27027370bffcSMatthew Dillon
27037370bffcSMatthew Dillon #if defined(__DragonFly__)
27047370bffcSMatthew Dillon error = bus_dmamap_load_mbuf_segment(ring->data_dmat, data->map, buf->m,
27057370bffcSMatthew Dillon segs, 1, &nsegs, BUS_DMA_NOWAIT);
27067370bffcSMatthew Dillon #else
27077370bffcSMatthew Dillon error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, buf->m,
27087370bffcSMatthew Dillon segs, &nsegs, BUS_DMA_NOWAIT);
27097370bffcSMatthew Dillon #endif
27107370bffcSMatthew Dillon if (error != 0 && error != EFBIG) {
27117370bffcSMatthew Dillon device_printf(sc->sc_dev,
27127370bffcSMatthew Dillon "%s: can't map mbuf (error %d)\n", __func__, error);
27137370bffcSMatthew Dillon goto end;
27147370bffcSMatthew Dillon }
27157370bffcSMatthew Dillon if (error != 0) {
27167370bffcSMatthew Dillon /* Too many DMA segments, linearize mbuf. */
27177370bffcSMatthew Dillon #if defined(__DragonFly__)
27187370bffcSMatthew Dillon m1 = m_defrag(buf->m, M_NOWAIT);
27197370bffcSMatthew Dillon #else
27207370bffcSMatthew Dillon m1 = m_collapse(buf->m, M_NOWAIT, WPI_MAX_SCATTER - 1);
27217370bffcSMatthew Dillon #endif
27227370bffcSMatthew Dillon if (m1 == NULL) {
27237370bffcSMatthew Dillon device_printf(sc->sc_dev,
27247370bffcSMatthew Dillon "%s: could not defrag mbuf\n", __func__);
27257370bffcSMatthew Dillon error = ENOBUFS;
27267370bffcSMatthew Dillon goto end;
27277370bffcSMatthew Dillon }
27287370bffcSMatthew Dillon buf->m = m1;
27297370bffcSMatthew Dillon
27307370bffcSMatthew Dillon #if defined(__DragonFly__)
27317370bffcSMatthew Dillon error = bus_dmamap_load_mbuf_segment(ring->data_dmat, data->map,
27327370bffcSMatthew Dillon buf->m, segs, 1, &nsegs, BUS_DMA_NOWAIT);
27337370bffcSMatthew Dillon #else
27347370bffcSMatthew Dillon error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map,
27357370bffcSMatthew Dillon buf->m, segs, &nsegs, BUS_DMA_NOWAIT);
27367370bffcSMatthew Dillon #endif
27377370bffcSMatthew Dillon if (__predict_false(error != 0)) {
27387370bffcSMatthew Dillon /* XXX fix this (applicable to the iwn(4) too) */
27397370bffcSMatthew Dillon /*
27407370bffcSMatthew Dillon * NB: Do not return error;
27417370bffcSMatthew Dillon * original mbuf does not exist anymore.
27427370bffcSMatthew Dillon */
27437370bffcSMatthew Dillon device_printf(sc->sc_dev,
27447370bffcSMatthew Dillon "%s: can't map mbuf (error %d)\n", __func__,
27457370bffcSMatthew Dillon error);
27467370bffcSMatthew Dillon if (ring->qid < WPI_CMD_QUEUE_NUM) {
27477370bffcSMatthew Dillon if_inc_counter(buf->ni->ni_vap->iv_ifp,
27487370bffcSMatthew Dillon IFCOUNTER_OERRORS, 1);
27497370bffcSMatthew Dillon if (!frag)
27507370bffcSMatthew Dillon ieee80211_free_node(buf->ni);
27517370bffcSMatthew Dillon }
27527370bffcSMatthew Dillon m_freem(buf->m);
27537370bffcSMatthew Dillon error = 0;
27547370bffcSMatthew Dillon goto end;
27557370bffcSMatthew Dillon }
27567370bffcSMatthew Dillon }
27577370bffcSMatthew Dillon
27587370bffcSMatthew Dillon KASSERT(nsegs < WPI_MAX_SCATTER,
27597370bffcSMatthew Dillon ("too many DMA segments, nsegs (%d) should be less than %d",
27607370bffcSMatthew Dillon nsegs, WPI_MAX_SCATTER));
27617370bffcSMatthew Dillon
27627370bffcSMatthew Dillon data->m = buf->m;
27637370bffcSMatthew Dillon data->ni = buf->ni;
27647370bffcSMatthew Dillon
27657370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n",
27667370bffcSMatthew Dillon __func__, ring->qid, cur, totlen, nsegs);
27677370bffcSMatthew Dillon
27687370bffcSMatthew Dillon /* Fill TX descriptor. */
27697370bffcSMatthew Dillon desc->nsegs = WPI_PAD32(totlen + pad) << 4 | (1 + nsegs);
27707370bffcSMatthew Dillon /* First DMA segment is used by the TX command. */
27717370bffcSMatthew Dillon desc->segs[0].addr = htole32(data->cmd_paddr);
27727370bffcSMatthew Dillon desc->segs[0].len = htole32(4 + buf->size + hdrlen + pad);
27737370bffcSMatthew Dillon /* Other DMA segments are for data payload. */
27747370bffcSMatthew Dillon seg = &segs[0];
27757370bffcSMatthew Dillon for (i = 1; i <= nsegs; i++) {
27767370bffcSMatthew Dillon desc->segs[i].addr = htole32(seg->ds_addr);
27777370bffcSMatthew Dillon desc->segs[i].len = htole32(seg->ds_len);
27787370bffcSMatthew Dillon seg++;
27797370bffcSMatthew Dillon }
27807370bffcSMatthew Dillon
27817370bffcSMatthew Dillon bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
27827370bffcSMatthew Dillon bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
27837370bffcSMatthew Dillon BUS_DMASYNC_PREWRITE);
27847370bffcSMatthew Dillon bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
27857370bffcSMatthew Dillon BUS_DMASYNC_PREWRITE);
27867370bffcSMatthew Dillon
27877370bffcSMatthew Dillon ring->pending += 1;
27887370bffcSMatthew Dillon
27897370bffcSMatthew Dillon if (!frag) {
27907370bffcSMatthew Dillon if (ring->qid < WPI_CMD_QUEUE_NUM) {
27917370bffcSMatthew Dillon WPI_TXQ_STATE_LOCK(sc);
27927370bffcSMatthew Dillon ring->queued += ring->pending;
27937370bffcSMatthew Dillon callout_reset(&sc->tx_timeout, 5*hz, wpi_tx_timeout,
27947370bffcSMatthew Dillon sc);
27957370bffcSMatthew Dillon WPI_TXQ_STATE_UNLOCK(sc);
27967370bffcSMatthew Dillon }
27977370bffcSMatthew Dillon
27987370bffcSMatthew Dillon /* Kick TX ring. */
27997370bffcSMatthew Dillon ring->cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT;
28007370bffcSMatthew Dillon ring->pending = 0;
28017370bffcSMatthew Dillon sc->sc_update_tx_ring(sc, ring);
28027370bffcSMatthew Dillon } else
28037370bffcSMatthew Dillon ieee80211_node_incref(data->ni);
28047370bffcSMatthew Dillon
28057370bffcSMatthew Dillon end: DPRINTF(sc, WPI_DEBUG_TRACE, error ? TRACE_STR_END_ERR : TRACE_STR_END,
28067370bffcSMatthew Dillon __func__);
28077370bffcSMatthew Dillon
28087370bffcSMatthew Dillon WPI_TXQ_UNLOCK(sc);
28097370bffcSMatthew Dillon
28107370bffcSMatthew Dillon return (error);
28117370bffcSMatthew Dillon }
28127370bffcSMatthew Dillon
28137370bffcSMatthew Dillon /*
28147370bffcSMatthew Dillon * Construct the data packet for a transmit buffer.
28157370bffcSMatthew Dillon */
28167370bffcSMatthew Dillon static int
wpi_tx_data(struct wpi_softc * sc,struct mbuf * m,struct ieee80211_node * ni)28177370bffcSMatthew Dillon wpi_tx_data(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
28187370bffcSMatthew Dillon {
28197370bffcSMatthew Dillon const struct ieee80211_txparam *tp;
28207370bffcSMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
28217370bffcSMatthew Dillon struct ieee80211com *ic = ni->ni_ic;
28227370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
28237370bffcSMatthew Dillon struct ieee80211_channel *chan;
28247370bffcSMatthew Dillon struct ieee80211_frame *wh;
28257370bffcSMatthew Dillon struct ieee80211_key *k = NULL;
28267370bffcSMatthew Dillon struct wpi_buf tx_data;
28277370bffcSMatthew Dillon struct wpi_cmd_data *tx = (struct wpi_cmd_data *)&tx_data.data;
28287370bffcSMatthew Dillon uint32_t flags;
28297370bffcSMatthew Dillon uint16_t ac, qos;
28307370bffcSMatthew Dillon uint8_t tid, type, rate;
28317370bffcSMatthew Dillon int swcrypt, ismcast, totlen;
28327370bffcSMatthew Dillon
28337370bffcSMatthew Dillon wh = mtod(m, struct ieee80211_frame *);
28347370bffcSMatthew Dillon type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
28357370bffcSMatthew Dillon ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
28367370bffcSMatthew Dillon swcrypt = 1;
28377370bffcSMatthew Dillon
28387370bffcSMatthew Dillon /* Select EDCA Access Category and TX ring for this frame. */
28397370bffcSMatthew Dillon if (IEEE80211_QOS_HAS_SEQ(wh)) {
28407370bffcSMatthew Dillon qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0];
28417370bffcSMatthew Dillon tid = qos & IEEE80211_QOS_TID;
28427370bffcSMatthew Dillon } else {
28437370bffcSMatthew Dillon qos = 0;
28447370bffcSMatthew Dillon tid = 0;
28457370bffcSMatthew Dillon }
28467370bffcSMatthew Dillon ac = M_WME_GETAC(m);
28477370bffcSMatthew Dillon
28487370bffcSMatthew Dillon chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ?
28497370bffcSMatthew Dillon ni->ni_chan : ic->ic_curchan;
28507370bffcSMatthew Dillon tp = &vap->iv_txparms[ieee80211_chan2mode(chan)];
28517370bffcSMatthew Dillon
28527370bffcSMatthew Dillon /* Choose a TX rate index. */
28537370bffcSMatthew Dillon if (type == IEEE80211_FC0_TYPE_MGT)
28547370bffcSMatthew Dillon rate = tp->mgmtrate;
28557370bffcSMatthew Dillon else if (ismcast)
28567370bffcSMatthew Dillon rate = tp->mcastrate;
28577370bffcSMatthew Dillon else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
28587370bffcSMatthew Dillon rate = tp->ucastrate;
28597370bffcSMatthew Dillon else if (m->m_flags & M_EAPOL)
28607370bffcSMatthew Dillon rate = tp->mgmtrate;
28617370bffcSMatthew Dillon else {
28627370bffcSMatthew Dillon /* XXX pass pktlen */
28637370bffcSMatthew Dillon (void) ieee80211_ratectl_rate(ni, NULL, 0);
28647370bffcSMatthew Dillon rate = ni->ni_txrate;
28657370bffcSMatthew Dillon }
28667370bffcSMatthew Dillon
28677370bffcSMatthew Dillon /* Encrypt the frame if need be. */
28687370bffcSMatthew Dillon if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
28697370bffcSMatthew Dillon /* Retrieve key for TX. */
28707370bffcSMatthew Dillon k = ieee80211_crypto_encap(ni, m);
28717370bffcSMatthew Dillon if (k == NULL)
28727370bffcSMatthew Dillon return (ENOBUFS);
28737370bffcSMatthew Dillon
28747370bffcSMatthew Dillon swcrypt = k->wk_flags & IEEE80211_KEY_SWCRYPT;
28757370bffcSMatthew Dillon
28767370bffcSMatthew Dillon /* 802.11 header may have moved. */
28777370bffcSMatthew Dillon wh = mtod(m, struct ieee80211_frame *);
28787370bffcSMatthew Dillon }
28797370bffcSMatthew Dillon totlen = m->m_pkthdr.len;
28807370bffcSMatthew Dillon
28817370bffcSMatthew Dillon if (ieee80211_radiotap_active_vap(vap)) {
28827370bffcSMatthew Dillon struct wpi_tx_radiotap_header *tap = &sc->sc_txtap;
28837370bffcSMatthew Dillon
28847370bffcSMatthew Dillon tap->wt_flags = 0;
28857370bffcSMatthew Dillon tap->wt_rate = rate;
28867370bffcSMatthew Dillon if (k != NULL)
28877370bffcSMatthew Dillon tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
28887370bffcSMatthew Dillon if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
28897370bffcSMatthew Dillon tap->wt_flags |= IEEE80211_RADIOTAP_F_FRAG;
28907370bffcSMatthew Dillon
28917370bffcSMatthew Dillon ieee80211_radiotap_tx(vap, m);
28927370bffcSMatthew Dillon }
28937370bffcSMatthew Dillon
28947370bffcSMatthew Dillon flags = 0;
28954db7dd1bSJoe Talbott if (!ismcast) {
28967370bffcSMatthew Dillon /* Unicast frame, check if an ACK is expected. */
28977370bffcSMatthew Dillon if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) !=
28987370bffcSMatthew Dillon IEEE80211_QOS_ACKPOLICY_NOACK)
28997370bffcSMatthew Dillon flags |= WPI_TX_NEED_ACK;
29004db7dd1bSJoe Talbott }
29017370bffcSMatthew Dillon
29027370bffcSMatthew Dillon if (!IEEE80211_QOS_HAS_SEQ(wh))
29037370bffcSMatthew Dillon flags |= WPI_TX_AUTO_SEQ;
29047370bffcSMatthew Dillon if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
29057370bffcSMatthew Dillon flags |= WPI_TX_MORE_FRAG;
29067370bffcSMatthew Dillon
29077370bffcSMatthew Dillon /* Check if frame must be protected using RTS/CTS or CTS-to-self. */
29087370bffcSMatthew Dillon if (!ismcast) {
29097370bffcSMatthew Dillon /* NB: Group frames are sent using CCK in 802.11b/g. */
29107370bffcSMatthew Dillon if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) {
29117370bffcSMatthew Dillon flags |= WPI_TX_NEED_RTS;
29127370bffcSMatthew Dillon } else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
29137370bffcSMatthew Dillon WPI_RATE_IS_OFDM(rate)) {
29147370bffcSMatthew Dillon if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
29157370bffcSMatthew Dillon flags |= WPI_TX_NEED_CTS;
29167370bffcSMatthew Dillon else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
29177370bffcSMatthew Dillon flags |= WPI_TX_NEED_RTS;
29184db7dd1bSJoe Talbott }
29197370bffcSMatthew Dillon
29207370bffcSMatthew Dillon if (flags & (WPI_TX_NEED_RTS | WPI_TX_NEED_CTS))
29217370bffcSMatthew Dillon flags |= WPI_TX_FULL_TXOP;
29227370bffcSMatthew Dillon }
29237370bffcSMatthew Dillon
29247370bffcSMatthew Dillon memset(tx, 0, sizeof (struct wpi_cmd_data));
29257370bffcSMatthew Dillon if (type == IEEE80211_FC0_TYPE_MGT) {
29264db7dd1bSJoe Talbott uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
29277370bffcSMatthew Dillon
29287370bffcSMatthew Dillon /* Tell HW to set timestamp in probe responses. */
29294db7dd1bSJoe Talbott if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
29307370bffcSMatthew Dillon flags |= WPI_TX_INSERT_TSTAMP;
29314db7dd1bSJoe Talbott if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
29324db7dd1bSJoe Talbott subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
29334db7dd1bSJoe Talbott tx->timeout = htole16(3);
29344db7dd1bSJoe Talbott else
29354db7dd1bSJoe Talbott tx->timeout = htole16(2);
29364db7dd1bSJoe Talbott }
29374db7dd1bSJoe Talbott
29387370bffcSMatthew Dillon if (ismcast || type != IEEE80211_FC0_TYPE_DATA)
29397370bffcSMatthew Dillon tx->id = WPI_ID_BROADCAST;
29407370bffcSMatthew Dillon else {
29417370bffcSMatthew Dillon if (wn->id == WPI_ID_UNDEFINED) {
29427370bffcSMatthew Dillon device_printf(sc->sc_dev,
29437370bffcSMatthew Dillon "%s: undefined node id\n", __func__);
29447370bffcSMatthew Dillon return (EINVAL);
29457370bffcSMatthew Dillon }
29467370bffcSMatthew Dillon
29477370bffcSMatthew Dillon tx->id = wn->id;
29487370bffcSMatthew Dillon }
29497370bffcSMatthew Dillon
29507370bffcSMatthew Dillon if (!swcrypt) {
29517370bffcSMatthew Dillon switch (k->wk_cipher->ic_cipher) {
29527370bffcSMatthew Dillon case IEEE80211_CIPHER_AES_CCM:
29537370bffcSMatthew Dillon tx->security = WPI_CIPHER_CCMP;
29547370bffcSMatthew Dillon break;
29557370bffcSMatthew Dillon
29567370bffcSMatthew Dillon default:
29577370bffcSMatthew Dillon break;
29587370bffcSMatthew Dillon }
29597370bffcSMatthew Dillon
29607370bffcSMatthew Dillon memcpy(tx->key, k->wk_key, k->wk_keylen);
29617370bffcSMatthew Dillon }
29627370bffcSMatthew Dillon
29637370bffcSMatthew Dillon if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) {
29647370bffcSMatthew Dillon struct mbuf *next = m->m_nextpkt;
29657370bffcSMatthew Dillon
29667370bffcSMatthew Dillon tx->lnext = htole16(next->m_pkthdr.len);
29677370bffcSMatthew Dillon tx->fnext = htole32(tx->security |
29687370bffcSMatthew Dillon (flags & WPI_TX_NEED_ACK) |
29697370bffcSMatthew Dillon WPI_NEXT_STA_ID(tx->id));
29707370bffcSMatthew Dillon }
29717370bffcSMatthew Dillon
29727370bffcSMatthew Dillon tx->len = htole16(totlen);
29737370bffcSMatthew Dillon tx->flags = htole32(flags);
29747370bffcSMatthew Dillon tx->plcp = rate2plcp(rate);
29757370bffcSMatthew Dillon tx->tid = tid;
29767370bffcSMatthew Dillon tx->lifetime = htole32(WPI_LIFETIME_INFINITE);
29777370bffcSMatthew Dillon tx->ofdm_mask = 0xff;
29787370bffcSMatthew Dillon tx->cck_mask = 0x0f;
29797370bffcSMatthew Dillon tx->rts_ntries = 7;
29804db7dd1bSJoe Talbott tx->data_ntries = tp->maxretry;
29817370bffcSMatthew Dillon
29827370bffcSMatthew Dillon tx_data.ni = ni;
29837370bffcSMatthew Dillon tx_data.m = m;
29847370bffcSMatthew Dillon tx_data.size = sizeof(struct wpi_cmd_data);
29857370bffcSMatthew Dillon tx_data.code = WPI_CMD_TX_DATA;
29867370bffcSMatthew Dillon tx_data.ac = ac;
29877370bffcSMatthew Dillon
29887370bffcSMatthew Dillon return wpi_cmd2(sc, &tx_data);
29897370bffcSMatthew Dillon }
29907370bffcSMatthew Dillon
29917370bffcSMatthew Dillon static int
wpi_tx_data_raw(struct wpi_softc * sc,struct mbuf * m,struct ieee80211_node * ni,const struct ieee80211_bpf_params * params)29927370bffcSMatthew Dillon wpi_tx_data_raw(struct wpi_softc *sc, struct mbuf *m,
29937370bffcSMatthew Dillon struct ieee80211_node *ni, const struct ieee80211_bpf_params *params)
29947370bffcSMatthew Dillon {
29957370bffcSMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
29967370bffcSMatthew Dillon struct ieee80211_key *k = NULL;
29977370bffcSMatthew Dillon struct ieee80211_frame *wh;
29987370bffcSMatthew Dillon struct wpi_buf tx_data;
29997370bffcSMatthew Dillon struct wpi_cmd_data *tx = (struct wpi_cmd_data *)&tx_data.data;
30007370bffcSMatthew Dillon uint32_t flags;
30017370bffcSMatthew Dillon uint8_t ac, type, rate;
30027370bffcSMatthew Dillon int swcrypt, totlen;
30037370bffcSMatthew Dillon
30047370bffcSMatthew Dillon wh = mtod(m, struct ieee80211_frame *);
30057370bffcSMatthew Dillon type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
30067370bffcSMatthew Dillon swcrypt = 1;
30077370bffcSMatthew Dillon
30087370bffcSMatthew Dillon ac = params->ibp_pri & 3;
30097370bffcSMatthew Dillon
30107370bffcSMatthew Dillon /* Choose a TX rate index. */
30117370bffcSMatthew Dillon rate = params->ibp_rate0;
30127370bffcSMatthew Dillon
30137370bffcSMatthew Dillon flags = 0;
30147370bffcSMatthew Dillon if (!IEEE80211_QOS_HAS_SEQ(wh))
30157370bffcSMatthew Dillon flags |= WPI_TX_AUTO_SEQ;
30167370bffcSMatthew Dillon if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
30177370bffcSMatthew Dillon flags |= WPI_TX_NEED_ACK;
30187370bffcSMatthew Dillon if (params->ibp_flags & IEEE80211_BPF_RTS)
30197370bffcSMatthew Dillon flags |= WPI_TX_NEED_RTS;
30207370bffcSMatthew Dillon if (params->ibp_flags & IEEE80211_BPF_CTS)
30217370bffcSMatthew Dillon flags |= WPI_TX_NEED_CTS;
30227370bffcSMatthew Dillon if (flags & (WPI_TX_NEED_RTS | WPI_TX_NEED_CTS))
30237370bffcSMatthew Dillon flags |= WPI_TX_FULL_TXOP;
30247370bffcSMatthew Dillon
30257370bffcSMatthew Dillon /* Encrypt the frame if need be. */
30267370bffcSMatthew Dillon if (params->ibp_flags & IEEE80211_BPF_CRYPTO) {
30277370bffcSMatthew Dillon /* Retrieve key for TX. */
30287370bffcSMatthew Dillon k = ieee80211_crypto_encap(ni, m);
30297370bffcSMatthew Dillon if (k == NULL)
30307370bffcSMatthew Dillon return (ENOBUFS);
30317370bffcSMatthew Dillon
30327370bffcSMatthew Dillon swcrypt = k->wk_flags & IEEE80211_KEY_SWCRYPT;
30337370bffcSMatthew Dillon
30347370bffcSMatthew Dillon /* 802.11 header may have moved. */
30357370bffcSMatthew Dillon wh = mtod(m, struct ieee80211_frame *);
30367370bffcSMatthew Dillon }
30377370bffcSMatthew Dillon totlen = m->m_pkthdr.len;
30384db7dd1bSJoe Talbott
30394db7dd1bSJoe Talbott if (ieee80211_radiotap_active_vap(vap)) {
30404db7dd1bSJoe Talbott struct wpi_tx_radiotap_header *tap = &sc->sc_txtap;
30417370bffcSMatthew Dillon
30424db7dd1bSJoe Talbott tap->wt_flags = 0;
30434db7dd1bSJoe Talbott tap->wt_rate = rate;
30447370bffcSMatthew Dillon if (params->ibp_flags & IEEE80211_BPF_CRYPTO)
30454db7dd1bSJoe Talbott tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
30464db7dd1bSJoe Talbott
30477370bffcSMatthew Dillon ieee80211_radiotap_tx(vap, m);
30484db7dd1bSJoe Talbott }
30494db7dd1bSJoe Talbott
30507370bffcSMatthew Dillon memset(tx, 0, sizeof (struct wpi_cmd_data));
30517370bffcSMatthew Dillon if (type == IEEE80211_FC0_TYPE_MGT) {
30527370bffcSMatthew Dillon uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
30534db7dd1bSJoe Talbott
30547370bffcSMatthew Dillon /* Tell HW to set timestamp in probe responses. */
30557370bffcSMatthew Dillon if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
30567370bffcSMatthew Dillon flags |= WPI_TX_INSERT_TSTAMP;
30577370bffcSMatthew Dillon if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ ||
30587370bffcSMatthew Dillon subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ)
30597370bffcSMatthew Dillon tx->timeout = htole16(3);
30607370bffcSMatthew Dillon else
30617370bffcSMatthew Dillon tx->timeout = htole16(2);
30624db7dd1bSJoe Talbott }
30634db7dd1bSJoe Talbott
30647370bffcSMatthew Dillon if (!swcrypt) {
30657370bffcSMatthew Dillon switch (k->wk_cipher->ic_cipher) {
30667370bffcSMatthew Dillon case IEEE80211_CIPHER_AES_CCM:
30677370bffcSMatthew Dillon tx->security = WPI_CIPHER_CCMP;
30687370bffcSMatthew Dillon break;
30694db7dd1bSJoe Talbott
30707370bffcSMatthew Dillon default:
30717370bffcSMatthew Dillon break;
30724db7dd1bSJoe Talbott }
30734db7dd1bSJoe Talbott
30747370bffcSMatthew Dillon memcpy(tx->key, k->wk_key, k->wk_keylen);
30754db7dd1bSJoe Talbott }
30764db7dd1bSJoe Talbott
30777370bffcSMatthew Dillon tx->len = htole16(totlen);
30787370bffcSMatthew Dillon tx->flags = htole32(flags);
30797370bffcSMatthew Dillon tx->plcp = rate2plcp(rate);
30807370bffcSMatthew Dillon tx->id = WPI_ID_BROADCAST;
30817370bffcSMatthew Dillon tx->lifetime = htole32(WPI_LIFETIME_INFINITE);
30827370bffcSMatthew Dillon tx->rts_ntries = params->ibp_try1;
30837370bffcSMatthew Dillon tx->data_ntries = params->ibp_try0;
30847370bffcSMatthew Dillon
30857370bffcSMatthew Dillon tx_data.ni = ni;
30867370bffcSMatthew Dillon tx_data.m = m;
30877370bffcSMatthew Dillon tx_data.size = sizeof(struct wpi_cmd_data);
30887370bffcSMatthew Dillon tx_data.code = WPI_CMD_TX_DATA;
30897370bffcSMatthew Dillon tx_data.ac = ac;
30907370bffcSMatthew Dillon
30917370bffcSMatthew Dillon return wpi_cmd2(sc, &tx_data);
30927370bffcSMatthew Dillon }
30937370bffcSMatthew Dillon
30947370bffcSMatthew Dillon static __inline int
wpi_tx_ring_free_space(struct wpi_softc * sc,uint16_t ac)30957370bffcSMatthew Dillon wpi_tx_ring_free_space(struct wpi_softc *sc, uint16_t ac)
30964db7dd1bSJoe Talbott {
30977370bffcSMatthew Dillon struct wpi_tx_ring *ring = &sc->txq[ac];
30987370bffcSMatthew Dillon int retval;
3099d0c50e91SJohannes Hofmann
31007370bffcSMatthew Dillon WPI_TXQ_STATE_LOCK(sc);
31017370bffcSMatthew Dillon retval = WPI_TX_RING_HIMARK - ring->queued;
31027370bffcSMatthew Dillon WPI_TXQ_STATE_UNLOCK(sc);
3103c2ccdce4SJohannes Hofmann
31047370bffcSMatthew Dillon return retval;
31054db7dd1bSJoe Talbott }
31064db7dd1bSJoe Talbott
31074db7dd1bSJoe Talbott static int
wpi_raw_xmit(struct ieee80211_node * ni,struct mbuf * m,const struct ieee80211_bpf_params * params)31084db7dd1bSJoe Talbott wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
31094db7dd1bSJoe Talbott const struct ieee80211_bpf_params *params)
31104db7dd1bSJoe Talbott {
31114db7dd1bSJoe Talbott struct ieee80211com *ic = ni->ni_ic;
31124f1aaf2fSImre Vadász struct wpi_softc *sc = ic->ic_softc;
31137370bffcSMatthew Dillon uint16_t ac;
31147370bffcSMatthew Dillon int error = 0;
31154db7dd1bSJoe Talbott
31167370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
31177370bffcSMatthew Dillon
31187370bffcSMatthew Dillon ac = M_WME_GETAC(m);
31197370bffcSMatthew Dillon
31207370bffcSMatthew Dillon WPI_TX_LOCK(sc);
31217370bffcSMatthew Dillon
31227370bffcSMatthew Dillon /* NB: no fragments here */
31237370bffcSMatthew Dillon if (sc->sc_running == 0 || wpi_tx_ring_free_space(sc, ac) < 1) {
31247370bffcSMatthew Dillon error = sc->sc_running ? ENOBUFS : ENETDOWN;
31257370bffcSMatthew Dillon goto unlock;
31267370bffcSMatthew Dillon }
31277370bffcSMatthew Dillon
31287370bffcSMatthew Dillon if (params == NULL) {
31297370bffcSMatthew Dillon /*
31307370bffcSMatthew Dillon * Legacy path; interpret frame contents to decide
31317370bffcSMatthew Dillon * precisely how to send the frame.
31327370bffcSMatthew Dillon */
31337370bffcSMatthew Dillon error = wpi_tx_data(sc, m, ni);
31347370bffcSMatthew Dillon } else {
31357370bffcSMatthew Dillon /*
31367370bffcSMatthew Dillon * Caller supplied explicit parameters to use in
31377370bffcSMatthew Dillon * sending the frame.
31387370bffcSMatthew Dillon */
31397370bffcSMatthew Dillon error = wpi_tx_data_raw(sc, m, ni, params);
31407370bffcSMatthew Dillon }
31417370bffcSMatthew Dillon
31427370bffcSMatthew Dillon unlock: WPI_TX_UNLOCK(sc);
31437370bffcSMatthew Dillon
31447370bffcSMatthew Dillon if (error != 0) {
31454db7dd1bSJoe Talbott m_freem(m);
31467370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__);
31474db7dd1bSJoe Talbott
31484db7dd1bSJoe Talbott return error;
31494db7dd1bSJoe Talbott }
31504db7dd1bSJoe Talbott
31517370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
31527370bffcSMatthew Dillon
31537370bffcSMatthew Dillon return 0;
31547370bffcSMatthew Dillon }
31557370bffcSMatthew Dillon
31567370bffcSMatthew Dillon static int
wpi_transmit(struct ieee80211com * ic,struct mbuf * m)31577370bffcSMatthew Dillon wpi_transmit(struct ieee80211com *ic, struct mbuf *m)
31584db7dd1bSJoe Talbott {
31597370bffcSMatthew Dillon struct wpi_softc *sc = ic->ic_softc;
31607370bffcSMatthew Dillon struct ieee80211_node *ni;
31617370bffcSMatthew Dillon struct mbuf *mnext;
31627370bffcSMatthew Dillon uint16_t ac;
31637370bffcSMatthew Dillon int error, nmbufs;
31644db7dd1bSJoe Talbott
31657370bffcSMatthew Dillon WPI_TX_LOCK(sc);
31667370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_XMIT, "%s: called\n", __func__);
31674db7dd1bSJoe Talbott
31687370bffcSMatthew Dillon /* Check if interface is up & running. */
31697370bffcSMatthew Dillon if (__predict_false(sc->sc_running == 0)) {
31707370bffcSMatthew Dillon error = ENXIO;
31717370bffcSMatthew Dillon goto unlock;
31727370bffcSMatthew Dillon }
31734db7dd1bSJoe Talbott
31747370bffcSMatthew Dillon nmbufs = 1;
31757370bffcSMatthew Dillon for (mnext = m->m_nextpkt; mnext != NULL; mnext = mnext->m_nextpkt)
31767370bffcSMatthew Dillon nmbufs++;
31774db7dd1bSJoe Talbott
31787370bffcSMatthew Dillon /* Check for available space. */
31797370bffcSMatthew Dillon ac = M_WME_GETAC(m);
31807370bffcSMatthew Dillon if (wpi_tx_ring_free_space(sc, ac) < nmbufs) {
31817370bffcSMatthew Dillon error = ENOBUFS;
31827370bffcSMatthew Dillon goto unlock;
31837370bffcSMatthew Dillon }
31844db7dd1bSJoe Talbott
31857370bffcSMatthew Dillon error = 0;
31867370bffcSMatthew Dillon ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
31877370bffcSMatthew Dillon do {
31887370bffcSMatthew Dillon mnext = m->m_nextpkt;
31897370bffcSMatthew Dillon if (wpi_tx_data(sc, m, ni) != 0) {
3190c75fa8b8SMatthew Dillon /*
3191c75fa8b8SMatthew Dillon * Breakout if error, but we will return 0 (no error)
3192c75fa8b8SMatthew Dillon * for this case so we are responsible for freeing
3193c75fa8b8SMatthew Dillon * the mbuf and the node.
3194c75fa8b8SMatthew Dillon */
31957370bffcSMatthew Dillon if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS,
31967370bffcSMatthew Dillon nmbufs);
31977370bffcSMatthew Dillon wpi_free_txfrags(sc, ac);
31987370bffcSMatthew Dillon ieee80211_free_mbuf(m);
31997370bffcSMatthew Dillon ieee80211_free_node(ni);
32007370bffcSMatthew Dillon break;
32017370bffcSMatthew Dillon }
32027370bffcSMatthew Dillon } while((m = mnext) != NULL);
32037370bffcSMatthew Dillon
32047370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_XMIT, "%s: done\n", __func__);
32057370bffcSMatthew Dillon
32067370bffcSMatthew Dillon unlock: WPI_TX_UNLOCK(sc);
32077370bffcSMatthew Dillon
32087370bffcSMatthew Dillon return (error);
32097370bffcSMatthew Dillon }
32107370bffcSMatthew Dillon
32117370bffcSMatthew Dillon static void
wpi_watchdog_rfkill(void * arg)32127370bffcSMatthew Dillon wpi_watchdog_rfkill(void *arg)
32137370bffcSMatthew Dillon {
32147370bffcSMatthew Dillon struct wpi_softc *sc = arg;
32157370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
32167370bffcSMatthew Dillon
32177370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_WATCHDOG, "RFkill Watchdog: tick\n");
32187370bffcSMatthew Dillon
32197370bffcSMatthew Dillon /* No need to lock firmware memory. */
32207370bffcSMatthew Dillon if ((wpi_prph_read(sc, WPI_APMG_RFKILL) & 0x1) == 0) {
32217370bffcSMatthew Dillon /* Radio kill switch is still off. */
32227370bffcSMatthew Dillon callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill,
32237370bffcSMatthew Dillon sc);
32247370bffcSMatthew Dillon } else
32257370bffcSMatthew Dillon ieee80211_runtask(ic, &sc->sc_radioon_task);
32267370bffcSMatthew Dillon }
32277370bffcSMatthew Dillon
32287370bffcSMatthew Dillon static void
wpi_scan_timeout(void * arg)32297370bffcSMatthew Dillon wpi_scan_timeout(void *arg)
32307370bffcSMatthew Dillon {
32317370bffcSMatthew Dillon struct wpi_softc *sc = arg;
32327370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
32337370bffcSMatthew Dillon
32347370bffcSMatthew Dillon ic_printf(ic, "scan timeout\n");
32357370bffcSMatthew Dillon ieee80211_restart_all(ic);
32367370bffcSMatthew Dillon }
32377370bffcSMatthew Dillon
32387370bffcSMatthew Dillon static void
wpi_tx_timeout(void * arg)32397370bffcSMatthew Dillon wpi_tx_timeout(void *arg)
32407370bffcSMatthew Dillon {
32417370bffcSMatthew Dillon struct wpi_softc *sc = arg;
32427370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
32437370bffcSMatthew Dillon
32447370bffcSMatthew Dillon ic_printf(ic, "device timeout\n");
32457370bffcSMatthew Dillon ieee80211_restart_all(ic);
32467370bffcSMatthew Dillon }
32477370bffcSMatthew Dillon
32487370bffcSMatthew Dillon static void
wpi_parent(struct ieee80211com * ic)32497370bffcSMatthew Dillon wpi_parent(struct ieee80211com *ic)
32507370bffcSMatthew Dillon {
32517370bffcSMatthew Dillon struct wpi_softc *sc = ic->ic_softc;
32527370bffcSMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
32537370bffcSMatthew Dillon
32547370bffcSMatthew Dillon if (ic->ic_nrunning > 0) {
32557370bffcSMatthew Dillon if (wpi_init(sc) == 0) {
32567370bffcSMatthew Dillon ieee80211_notify_radio(ic, 1);
32577370bffcSMatthew Dillon ieee80211_start_all(ic);
32587370bffcSMatthew Dillon } else {
32597370bffcSMatthew Dillon ieee80211_notify_radio(ic, 0);
32607370bffcSMatthew Dillon ieee80211_stop(vap);
32617370bffcSMatthew Dillon }
32627370bffcSMatthew Dillon } else {
32637370bffcSMatthew Dillon ieee80211_notify_radio(ic, 0);
32647370bffcSMatthew Dillon wpi_stop(sc);
32657370bffcSMatthew Dillon }
32664db7dd1bSJoe Talbott }
32674db7dd1bSJoe Talbott
32684db7dd1bSJoe Talbott /*
32694db7dd1bSJoe Talbott * Send a command to the firmware.
32704db7dd1bSJoe Talbott */
32714db7dd1bSJoe Talbott static int
wpi_cmd(struct wpi_softc * sc,uint8_t code,const void * buf,uint16_t size,int async)32727370bffcSMatthew Dillon wpi_cmd(struct wpi_softc *sc, uint8_t code, const void *buf, uint16_t size,
32737370bffcSMatthew Dillon int async)
32744db7dd1bSJoe Talbott {
32757370bffcSMatthew Dillon struct wpi_tx_ring *ring = &sc->txq[WPI_CMD_QUEUE_NUM];
32764db7dd1bSJoe Talbott struct wpi_tx_desc *desc;
32777370bffcSMatthew Dillon struct wpi_tx_data *data;
32784db7dd1bSJoe Talbott struct wpi_tx_cmd *cmd;
32797370bffcSMatthew Dillon struct mbuf *m;
32807370bffcSMatthew Dillon bus_addr_t paddr;
32817370bffcSMatthew Dillon uint16_t totlen;
32827370bffcSMatthew Dillon int error;
32834db7dd1bSJoe Talbott
32847370bffcSMatthew Dillon WPI_TXQ_LOCK(sc);
32857370bffcSMatthew Dillon
32867370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
32877370bffcSMatthew Dillon
32887370bffcSMatthew Dillon if (__predict_false(sc->sc_running == 0)) {
32897370bffcSMatthew Dillon /* wpi_stop() was called */
32907370bffcSMatthew Dillon if (code == WPI_CMD_SCAN)
32917370bffcSMatthew Dillon error = ENETDOWN;
32927370bffcSMatthew Dillon else
32937370bffcSMatthew Dillon error = 0;
32947370bffcSMatthew Dillon
32957370bffcSMatthew Dillon goto fail;
32967370bffcSMatthew Dillon }
32977370bffcSMatthew Dillon
32987370bffcSMatthew Dillon if (async == 0)
3299d0c50e91SJohannes Hofmann WPI_LOCK_ASSERT(sc);
33004db7dd1bSJoe Talbott
33017370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_CMD, "%s: cmd %s size %u async %d\n",
33027370bffcSMatthew Dillon __func__, wpi_cmd_str(code), size, async);
33034db7dd1bSJoe Talbott
33044db7dd1bSJoe Talbott desc = &ring->desc[ring->cur];
33057370bffcSMatthew Dillon data = &ring->data[ring->cur];
33067370bffcSMatthew Dillon totlen = 4 + size;
33077370bffcSMatthew Dillon
33087370bffcSMatthew Dillon if (size > sizeof cmd->data) {
33097370bffcSMatthew Dillon /* Command is too large to fit in a descriptor. */
33107370bffcSMatthew Dillon if (totlen > MCLBYTES) {
33117370bffcSMatthew Dillon error = EINVAL;
33127370bffcSMatthew Dillon goto fail;
33137370bffcSMatthew Dillon }
33147370bffcSMatthew Dillon m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
33157370bffcSMatthew Dillon if (m == NULL) {
33167370bffcSMatthew Dillon error = ENOMEM;
33177370bffcSMatthew Dillon goto fail;
33187370bffcSMatthew Dillon }
33197370bffcSMatthew Dillon cmd = mtod(m, struct wpi_tx_cmd *);
33207370bffcSMatthew Dillon error = bus_dmamap_load(ring->data_dmat, data->map, cmd,
33217370bffcSMatthew Dillon totlen, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT);
33227370bffcSMatthew Dillon if (error != 0) {
33237370bffcSMatthew Dillon m_freem(m);
33247370bffcSMatthew Dillon goto fail;
33257370bffcSMatthew Dillon }
33267370bffcSMatthew Dillon data->m = m;
33277370bffcSMatthew Dillon } else {
33284db7dd1bSJoe Talbott cmd = &ring->cmd[ring->cur];
33297370bffcSMatthew Dillon paddr = data->cmd_paddr;
33307370bffcSMatthew Dillon }
33314db7dd1bSJoe Talbott
33324db7dd1bSJoe Talbott cmd->code = code;
33334db7dd1bSJoe Talbott cmd->flags = 0;
33344db7dd1bSJoe Talbott cmd->qid = ring->qid;
33354db7dd1bSJoe Talbott cmd->idx = ring->cur;
33364db7dd1bSJoe Talbott memcpy(cmd->data, buf, size);
33374db7dd1bSJoe Talbott
33387370bffcSMatthew Dillon desc->nsegs = 1 + (WPI_PAD32(size) << 4);
33397370bffcSMatthew Dillon desc->segs[0].addr = htole32(paddr);
33407370bffcSMatthew Dillon desc->segs[0].len = htole32(totlen);
33414db7dd1bSJoe Talbott
33427370bffcSMatthew Dillon if (size > sizeof cmd->data) {
33437370bffcSMatthew Dillon bus_dmamap_sync(ring->data_dmat, data->map,
33447370bffcSMatthew Dillon BUS_DMASYNC_PREWRITE);
33457370bffcSMatthew Dillon } else {
33467370bffcSMatthew Dillon bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map,
33477370bffcSMatthew Dillon BUS_DMASYNC_PREWRITE);
33484db7dd1bSJoe Talbott }
33497370bffcSMatthew Dillon bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
33507370bffcSMatthew Dillon BUS_DMASYNC_PREWRITE);
33517370bffcSMatthew Dillon
33527370bffcSMatthew Dillon /* Kick command ring. */
33537370bffcSMatthew Dillon ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT;
33547370bffcSMatthew Dillon sc->sc_update_tx_ring(sc, ring);
33557370bffcSMatthew Dillon
33567370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
33577370bffcSMatthew Dillon
33587370bffcSMatthew Dillon WPI_TXQ_UNLOCK(sc);
33594db7dd1bSJoe Talbott
3360d0c50e91SJohannes Hofmann #if defined(__DragonFly__)
33617370bffcSMatthew Dillon return async ? 0 : lksleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz);
3362d0c50e91SJohannes Hofmann #else
33637370bffcSMatthew Dillon return async ? 0 : mtx_sleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz);
3364d0c50e91SJohannes Hofmann #endif
33654db7dd1bSJoe Talbott
33667370bffcSMatthew Dillon fail: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__);
33674db7dd1bSJoe Talbott
33687370bffcSMatthew Dillon WPI_TXQ_UNLOCK(sc);
33694db7dd1bSJoe Talbott
33707370bffcSMatthew Dillon return error;
33714db7dd1bSJoe Talbott }
33724db7dd1bSJoe Talbott
33734db7dd1bSJoe Talbott /*
33747370bffcSMatthew Dillon * Configure HW multi-rate retries.
33754db7dd1bSJoe Talbott */
33764db7dd1bSJoe Talbott static int
wpi_mrr_setup(struct wpi_softc * sc)33774db7dd1bSJoe Talbott wpi_mrr_setup(struct wpi_softc *sc)
33784db7dd1bSJoe Talbott {
33797370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
33804db7dd1bSJoe Talbott struct wpi_mrr_setup mrr;
33817370bffcSMatthew Dillon uint8_t i;
33827370bffcSMatthew Dillon int error;
33834db7dd1bSJoe Talbott
33847370bffcSMatthew Dillon /* CCK rates (not used with 802.11a). */
33857370bffcSMatthew Dillon for (i = WPI_RIDX_CCK1; i <= WPI_RIDX_CCK11; i++) {
33864db7dd1bSJoe Talbott mrr.rates[i].flags = 0;
33877370bffcSMatthew Dillon mrr.rates[i].plcp = wpi_ridx_to_plcp[i];
33887370bffcSMatthew Dillon /* Fallback to the immediate lower CCK rate (if any.) */
33897370bffcSMatthew Dillon mrr.rates[i].next =
33907370bffcSMatthew Dillon (i == WPI_RIDX_CCK1) ? WPI_RIDX_CCK1 : i - 1;
33917370bffcSMatthew Dillon /* Try twice at this rate before falling back to "next". */
33927370bffcSMatthew Dillon mrr.rates[i].ntries = WPI_NTRIES_DEFAULT;
33934db7dd1bSJoe Talbott }
33947370bffcSMatthew Dillon /* OFDM rates (not used with 802.11b). */
33957370bffcSMatthew Dillon for (i = WPI_RIDX_OFDM6; i <= WPI_RIDX_OFDM54; i++) {
33964db7dd1bSJoe Talbott mrr.rates[i].flags = 0;
33977370bffcSMatthew Dillon mrr.rates[i].plcp = wpi_ridx_to_plcp[i];
33987370bffcSMatthew Dillon /* Fallback to the immediate lower rate (if any.) */
33997370bffcSMatthew Dillon /* We allow fallback from OFDM/6 to CCK/2 in 11b/g mode. */
34007370bffcSMatthew Dillon mrr.rates[i].next = (i == WPI_RIDX_OFDM6) ?
34014db7dd1bSJoe Talbott ((ic->ic_curmode == IEEE80211_MODE_11A) ?
34027370bffcSMatthew Dillon WPI_RIDX_OFDM6 : WPI_RIDX_CCK2) :
34034db7dd1bSJoe Talbott i - 1;
34047370bffcSMatthew Dillon /* Try twice at this rate before falling back to "next". */
34057370bffcSMatthew Dillon mrr.rates[i].ntries = WPI_NTRIES_DEFAULT;
34064db7dd1bSJoe Talbott }
34077370bffcSMatthew Dillon /* Setup MRR for control frames. */
34087370bffcSMatthew Dillon mrr.which = htole32(WPI_MRR_CTL);
34094db7dd1bSJoe Talbott error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0);
34104db7dd1bSJoe Talbott if (error != 0) {
34114db7dd1bSJoe Talbott device_printf(sc->sc_dev,
34124db7dd1bSJoe Talbott "could not setup MRR for control frames\n");
34134db7dd1bSJoe Talbott return error;
34144db7dd1bSJoe Talbott }
34157370bffcSMatthew Dillon /* Setup MRR for data frames. */
34167370bffcSMatthew Dillon mrr.which = htole32(WPI_MRR_DATA);
34174db7dd1bSJoe Talbott error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0);
34184db7dd1bSJoe Talbott if (error != 0) {
34194db7dd1bSJoe Talbott device_printf(sc->sc_dev,
34204db7dd1bSJoe Talbott "could not setup MRR for data frames\n");
34214db7dd1bSJoe Talbott return error;
34224db7dd1bSJoe Talbott }
34237370bffcSMatthew Dillon return 0;
34247370bffcSMatthew Dillon }
34257370bffcSMatthew Dillon
34267370bffcSMatthew Dillon static int
wpi_add_node(struct wpi_softc * sc,struct ieee80211_node * ni)34277370bffcSMatthew Dillon wpi_add_node(struct wpi_softc *sc, struct ieee80211_node *ni)
34287370bffcSMatthew Dillon {
34297370bffcSMatthew Dillon struct ieee80211com *ic = ni->ni_ic;
34307370bffcSMatthew Dillon struct wpi_vap *wvp = WPI_VAP(ni->ni_vap);
34317370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
34327370bffcSMatthew Dillon struct wpi_node_info node;
34337370bffcSMatthew Dillon int error;
34347370bffcSMatthew Dillon
34357370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
34367370bffcSMatthew Dillon
34377370bffcSMatthew Dillon if (wn->id == WPI_ID_UNDEFINED)
34387370bffcSMatthew Dillon return EINVAL;
34397370bffcSMatthew Dillon
34407370bffcSMatthew Dillon memset(&node, 0, sizeof node);
34417370bffcSMatthew Dillon IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr);
34427370bffcSMatthew Dillon node.id = wn->id;
34437370bffcSMatthew Dillon node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ?
34447370bffcSMatthew Dillon wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1];
34457370bffcSMatthew Dillon node.action = htole32(WPI_ACTION_SET_RATE);
34467370bffcSMatthew Dillon node.antenna = WPI_ANTENNA_BOTH;
34477370bffcSMatthew Dillon
34487370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_NODE, "%s: adding node %d (%s)\n", __func__,
34497370bffcSMatthew Dillon wn->id, ether_sprintf(ni->ni_macaddr));
34507370bffcSMatthew Dillon
34517370bffcSMatthew Dillon error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1);
34527370bffcSMatthew Dillon if (error != 0) {
34537370bffcSMatthew Dillon device_printf(sc->sc_dev,
34547370bffcSMatthew Dillon "%s: wpi_cmd() call failed with error code %d\n", __func__,
34557370bffcSMatthew Dillon error);
34567370bffcSMatthew Dillon return error;
34577370bffcSMatthew Dillon }
34587370bffcSMatthew Dillon
34597370bffcSMatthew Dillon if (wvp->wv_gtk != 0) {
34607370bffcSMatthew Dillon error = wpi_set_global_keys(ni);
34617370bffcSMatthew Dillon if (error != 0) {
34627370bffcSMatthew Dillon device_printf(sc->sc_dev,
34637370bffcSMatthew Dillon "%s: error while setting global keys\n", __func__);
34647370bffcSMatthew Dillon return ENXIO;
34657370bffcSMatthew Dillon }
34667370bffcSMatthew Dillon }
34674db7dd1bSJoe Talbott
34684db7dd1bSJoe Talbott return 0;
34694db7dd1bSJoe Talbott }
34704db7dd1bSJoe Talbott
34717370bffcSMatthew Dillon /*
34727370bffcSMatthew Dillon * Broadcast node is used to send group-addressed and management frames.
34737370bffcSMatthew Dillon */
34747370bffcSMatthew Dillon static int
wpi_add_broadcast_node(struct wpi_softc * sc,int async)34757370bffcSMatthew Dillon wpi_add_broadcast_node(struct wpi_softc *sc, int async)
34767370bffcSMatthew Dillon {
34777370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
34787370bffcSMatthew Dillon struct wpi_node_info node;
34797370bffcSMatthew Dillon
34807370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
34817370bffcSMatthew Dillon
34827370bffcSMatthew Dillon memset(&node, 0, sizeof node);
34837370bffcSMatthew Dillon IEEE80211_ADDR_COPY(node.macaddr, ieee80211broadcastaddr);
34847370bffcSMatthew Dillon node.id = WPI_ID_BROADCAST;
34857370bffcSMatthew Dillon node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ?
34867370bffcSMatthew Dillon wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1];
34877370bffcSMatthew Dillon node.action = htole32(WPI_ACTION_SET_RATE);
34887370bffcSMatthew Dillon node.antenna = WPI_ANTENNA_BOTH;
34897370bffcSMatthew Dillon
34907370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_NODE, "%s: adding broadcast node\n", __func__);
34917370bffcSMatthew Dillon
34927370bffcSMatthew Dillon return wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, async);
34937370bffcSMatthew Dillon }
34947370bffcSMatthew Dillon
34957370bffcSMatthew Dillon static int
wpi_add_sta_node(struct wpi_softc * sc,struct ieee80211_node * ni)34967370bffcSMatthew Dillon wpi_add_sta_node(struct wpi_softc *sc, struct ieee80211_node *ni)
34977370bffcSMatthew Dillon {
34987370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
34997370bffcSMatthew Dillon int error;
35007370bffcSMatthew Dillon
35017370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
35027370bffcSMatthew Dillon
35037370bffcSMatthew Dillon wn->id = wpi_add_node_entry_sta(sc);
35047370bffcSMatthew Dillon
35057370bffcSMatthew Dillon if ((error = wpi_add_node(sc, ni)) != 0) {
35067370bffcSMatthew Dillon wpi_del_node_entry(sc, wn->id);
35077370bffcSMatthew Dillon wn->id = WPI_ID_UNDEFINED;
35087370bffcSMatthew Dillon return error;
35097370bffcSMatthew Dillon }
35107370bffcSMatthew Dillon
35117370bffcSMatthew Dillon return 0;
35127370bffcSMatthew Dillon }
35137370bffcSMatthew Dillon
35147370bffcSMatthew Dillon static int
wpi_add_ibss_node(struct wpi_softc * sc,struct ieee80211_node * ni)35157370bffcSMatthew Dillon wpi_add_ibss_node(struct wpi_softc *sc, struct ieee80211_node *ni)
35167370bffcSMatthew Dillon {
35177370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
35187370bffcSMatthew Dillon int error;
35197370bffcSMatthew Dillon
35207370bffcSMatthew Dillon KASSERT(wn->id == WPI_ID_UNDEFINED,
35217370bffcSMatthew Dillon ("the node %d was added before", wn->id));
35227370bffcSMatthew Dillon
35237370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
35247370bffcSMatthew Dillon
35257370bffcSMatthew Dillon if ((wn->id = wpi_add_node_entry_adhoc(sc)) == WPI_ID_UNDEFINED) {
35267370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: h/w table is full\n", __func__);
35277370bffcSMatthew Dillon return ENOMEM;
35287370bffcSMatthew Dillon }
35297370bffcSMatthew Dillon
35307370bffcSMatthew Dillon if ((error = wpi_add_node(sc, ni)) != 0) {
35317370bffcSMatthew Dillon wpi_del_node_entry(sc, wn->id);
35327370bffcSMatthew Dillon wn->id = WPI_ID_UNDEFINED;
35337370bffcSMatthew Dillon return error;
35347370bffcSMatthew Dillon }
35357370bffcSMatthew Dillon
35367370bffcSMatthew Dillon return 0;
35377370bffcSMatthew Dillon }
35387370bffcSMatthew Dillon
35397370bffcSMatthew Dillon static void
wpi_del_node(struct wpi_softc * sc,struct ieee80211_node * ni)35407370bffcSMatthew Dillon wpi_del_node(struct wpi_softc *sc, struct ieee80211_node *ni)
35417370bffcSMatthew Dillon {
35427370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
35437370bffcSMatthew Dillon struct wpi_cmd_del_node node;
35447370bffcSMatthew Dillon int error;
35457370bffcSMatthew Dillon
35467370bffcSMatthew Dillon KASSERT(wn->id != WPI_ID_UNDEFINED, ("undefined node id passed"));
35477370bffcSMatthew Dillon
35487370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
35497370bffcSMatthew Dillon
35507370bffcSMatthew Dillon memset(&node, 0, sizeof node);
35517370bffcSMatthew Dillon IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr);
35527370bffcSMatthew Dillon node.count = 1;
35537370bffcSMatthew Dillon
35547370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_NODE, "%s: deleting node %d (%s)\n", __func__,
35557370bffcSMatthew Dillon wn->id, ether_sprintf(ni->ni_macaddr));
35567370bffcSMatthew Dillon
35577370bffcSMatthew Dillon error = wpi_cmd(sc, WPI_CMD_DEL_NODE, &node, sizeof node, 1);
35587370bffcSMatthew Dillon if (error != 0) {
35597370bffcSMatthew Dillon device_printf(sc->sc_dev,
35607370bffcSMatthew Dillon "%s: could not delete node %u, error %d\n", __func__,
35617370bffcSMatthew Dillon wn->id, error);
35627370bffcSMatthew Dillon }
35637370bffcSMatthew Dillon }
35647370bffcSMatthew Dillon
35657370bffcSMatthew Dillon static int
wpi_updateedca(struct ieee80211com * ic)35667370bffcSMatthew Dillon wpi_updateedca(struct ieee80211com *ic)
35677370bffcSMatthew Dillon {
35687370bffcSMatthew Dillon #define WPI_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */
35697370bffcSMatthew Dillon struct wpi_softc *sc = ic->ic_softc;
35707370bffcSMatthew Dillon struct wpi_edca_params cmd;
35717370bffcSMatthew Dillon int aci, error;
35727370bffcSMatthew Dillon
35737370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
35747370bffcSMatthew Dillon
35757370bffcSMatthew Dillon memset(&cmd, 0, sizeof cmd);
35767370bffcSMatthew Dillon cmd.flags = htole32(WPI_EDCA_UPDATE);
35777370bffcSMatthew Dillon for (aci = 0; aci < WME_NUM_AC; aci++) {
35787370bffcSMatthew Dillon const struct wmeParams *ac =
35797370bffcSMatthew Dillon &ic->ic_wme.wme_chanParams.cap_wmeParams[aci];
35807370bffcSMatthew Dillon cmd.ac[aci].aifsn = ac->wmep_aifsn;
35817370bffcSMatthew Dillon cmd.ac[aci].cwmin = htole16(WPI_EXP2(ac->wmep_logcwmin));
35827370bffcSMatthew Dillon cmd.ac[aci].cwmax = htole16(WPI_EXP2(ac->wmep_logcwmax));
35837370bffcSMatthew Dillon cmd.ac[aci].txoplimit =
35847370bffcSMatthew Dillon htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit));
35857370bffcSMatthew Dillon
35867370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_EDCA,
35877370bffcSMatthew Dillon "setting WME for queue %d aifsn=%d cwmin=%d cwmax=%d "
35887370bffcSMatthew Dillon "txoplimit=%d\n", aci, cmd.ac[aci].aifsn,
35897370bffcSMatthew Dillon cmd.ac[aci].cwmin, cmd.ac[aci].cwmax,
35907370bffcSMatthew Dillon cmd.ac[aci].txoplimit);
35917370bffcSMatthew Dillon }
35927370bffcSMatthew Dillon error = wpi_cmd(sc, WPI_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1);
35937370bffcSMatthew Dillon
35947370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
35957370bffcSMatthew Dillon
35967370bffcSMatthew Dillon return error;
35977370bffcSMatthew Dillon #undef WPI_EXP2
35987370bffcSMatthew Dillon }
35997370bffcSMatthew Dillon
36007370bffcSMatthew Dillon static void
wpi_set_promisc(struct wpi_softc * sc)36017370bffcSMatthew Dillon wpi_set_promisc(struct wpi_softc *sc)
36027370bffcSMatthew Dillon {
36037370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
36047370bffcSMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
36057370bffcSMatthew Dillon uint32_t promisc_filter;
36067370bffcSMatthew Dillon
36077370bffcSMatthew Dillon promisc_filter = WPI_FILTER_CTL;
36087370bffcSMatthew Dillon if (vap != NULL && vap->iv_opmode != IEEE80211_M_HOSTAP)
36097370bffcSMatthew Dillon promisc_filter |= WPI_FILTER_PROMISC;
36107370bffcSMatthew Dillon
36117370bffcSMatthew Dillon if (ic->ic_promisc > 0)
36127370bffcSMatthew Dillon sc->rxon.filter |= htole32(promisc_filter);
36137370bffcSMatthew Dillon else
36147370bffcSMatthew Dillon sc->rxon.filter &= ~htole32(promisc_filter);
36157370bffcSMatthew Dillon }
36167370bffcSMatthew Dillon
36177370bffcSMatthew Dillon static void
wpi_update_promisc(struct ieee80211com * ic)36187370bffcSMatthew Dillon wpi_update_promisc(struct ieee80211com *ic)
36197370bffcSMatthew Dillon {
36207370bffcSMatthew Dillon struct wpi_softc *sc = ic->ic_softc;
36217370bffcSMatthew Dillon
36227370bffcSMatthew Dillon WPI_LOCK(sc);
36237370bffcSMatthew Dillon if (sc->sc_running == 0) {
36247370bffcSMatthew Dillon WPI_UNLOCK(sc);
36257370bffcSMatthew Dillon return;
36267370bffcSMatthew Dillon }
36277370bffcSMatthew Dillon WPI_UNLOCK(sc);
36287370bffcSMatthew Dillon
36297370bffcSMatthew Dillon WPI_RXON_LOCK(sc);
36307370bffcSMatthew Dillon wpi_set_promisc(sc);
36317370bffcSMatthew Dillon
36327370bffcSMatthew Dillon if (wpi_send_rxon(sc, 1, 1) != 0) {
36337370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: could not send RXON\n",
36347370bffcSMatthew Dillon __func__);
36357370bffcSMatthew Dillon }
36367370bffcSMatthew Dillon WPI_RXON_UNLOCK(sc);
36377370bffcSMatthew Dillon }
36387370bffcSMatthew Dillon
36397370bffcSMatthew Dillon static void
wpi_update_mcast(struct ieee80211com * ic)36407370bffcSMatthew Dillon wpi_update_mcast(struct ieee80211com *ic)
36417370bffcSMatthew Dillon {
36427370bffcSMatthew Dillon /* Ignore */
36437370bffcSMatthew Dillon }
36447370bffcSMatthew Dillon
36454db7dd1bSJoe Talbott static void
wpi_set_led(struct wpi_softc * sc,uint8_t which,uint8_t off,uint8_t on)36464db7dd1bSJoe Talbott wpi_set_led(struct wpi_softc *sc, uint8_t which, uint8_t off, uint8_t on)
36474db7dd1bSJoe Talbott {
36484db7dd1bSJoe Talbott struct wpi_cmd_led led;
36494db7dd1bSJoe Talbott
36507370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
36517370bffcSMatthew Dillon
36524db7dd1bSJoe Talbott led.which = which;
36534db7dd1bSJoe Talbott led.unit = htole32(100000); /* on/off in unit of 100ms */
36544db7dd1bSJoe Talbott led.off = off;
36554db7dd1bSJoe Talbott led.on = on;
36564db7dd1bSJoe Talbott (void)wpi_cmd(sc, WPI_CMD_SET_LED, &led, sizeof led, 1);
36574db7dd1bSJoe Talbott }
36584db7dd1bSJoe Talbott
36597370bffcSMatthew Dillon static int
wpi_set_timing(struct wpi_softc * sc,struct ieee80211_node * ni)36607370bffcSMatthew Dillon wpi_set_timing(struct wpi_softc *sc, struct ieee80211_node *ni)
36614db7dd1bSJoe Talbott {
36627370bffcSMatthew Dillon struct wpi_cmd_timing cmd;
36634db7dd1bSJoe Talbott uint64_t val, mod;
36644db7dd1bSJoe Talbott
36657370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
36664db7dd1bSJoe Talbott
36677370bffcSMatthew Dillon memset(&cmd, 0, sizeof cmd);
36687370bffcSMatthew Dillon memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t));
36697370bffcSMatthew Dillon cmd.bintval = htole16(ni->ni_intval);
36707370bffcSMatthew Dillon cmd.lintval = htole16(10);
36714db7dd1bSJoe Talbott
36727370bffcSMatthew Dillon /* Compute remaining time until next beacon. */
36737370bffcSMatthew Dillon val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU;
36747370bffcSMatthew Dillon mod = le64toh(cmd.tstamp) % val;
36757370bffcSMatthew Dillon cmd.binitval = htole32((uint32_t)(val - mod));
36764db7dd1bSJoe Talbott
36777370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n",
36787370bffcSMatthew Dillon ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod));
36794db7dd1bSJoe Talbott
36807370bffcSMatthew Dillon return wpi_cmd(sc, WPI_CMD_TIMING, &cmd, sizeof cmd, 1);
36814db7dd1bSJoe Talbott }
36824db7dd1bSJoe Talbott
36834db7dd1bSJoe Talbott /*
36844db7dd1bSJoe Talbott * This function is called periodically (every 60 seconds) to adjust output
36854db7dd1bSJoe Talbott * power to temperature changes.
36864db7dd1bSJoe Talbott */
36874db7dd1bSJoe Talbott static void
wpi_power_calibration(struct wpi_softc * sc)36887370bffcSMatthew Dillon wpi_power_calibration(struct wpi_softc *sc)
36894db7dd1bSJoe Talbott {
36907370bffcSMatthew Dillon int temp;
36914db7dd1bSJoe Talbott
36927370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
36937370bffcSMatthew Dillon
36947370bffcSMatthew Dillon /* Update sensor data. */
36957370bffcSMatthew Dillon temp = (int)WPI_READ(sc, WPI_UCODE_GP2);
36967370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TEMP, "Temp in calibration is: %d\n", temp);
36977370bffcSMatthew Dillon
36987370bffcSMatthew Dillon /* Sanity-check read value. */
36994db7dd1bSJoe Talbott if (temp < -260 || temp > 25) {
37007370bffcSMatthew Dillon /* This can't be correct, ignore. */
37017370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TEMP,
37027370bffcSMatthew Dillon "out-of-range temperature reported: %d\n", temp);
37034db7dd1bSJoe Talbott return;
37044db7dd1bSJoe Talbott }
37054db7dd1bSJoe Talbott
37067370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TEMP, "temperature %d->%d\n", sc->temp, temp);
37074db7dd1bSJoe Talbott
37087370bffcSMatthew Dillon /* Adjust Tx power if need be. */
37094db7dd1bSJoe Talbott if (abs(temp - sc->temp) <= 6)
37104db7dd1bSJoe Talbott return;
37114db7dd1bSJoe Talbott
37124db7dd1bSJoe Talbott sc->temp = temp;
37134db7dd1bSJoe Talbott
37147370bffcSMatthew Dillon if (wpi_set_txpower(sc, 1) != 0) {
37154db7dd1bSJoe Talbott /* just warn, too bad for the automatic calibration... */
37164db7dd1bSJoe Talbott device_printf(sc->sc_dev,"could not adjust Tx power\n");
37174db7dd1bSJoe Talbott }
37184db7dd1bSJoe Talbott }
37194db7dd1bSJoe Talbott
37204db7dd1bSJoe Talbott /*
37217370bffcSMatthew Dillon * Set TX power for current channel.
37224db7dd1bSJoe Talbott */
37234db7dd1bSJoe Talbott static int
wpi_set_txpower(struct wpi_softc * sc,int async)37247370bffcSMatthew Dillon wpi_set_txpower(struct wpi_softc *sc, int async)
37254db7dd1bSJoe Talbott {
37264db7dd1bSJoe Talbott struct wpi_power_group *group;
37277370bffcSMatthew Dillon struct wpi_cmd_txpower cmd;
37287370bffcSMatthew Dillon uint8_t chan;
37297370bffcSMatthew Dillon int idx, is_chan_5ghz, i;
37304db7dd1bSJoe Talbott
37317370bffcSMatthew Dillon /* Retrieve current channel from last RXON. */
37327370bffcSMatthew Dillon chan = sc->rxon.chan;
37337370bffcSMatthew Dillon is_chan_5ghz = (sc->rxon.flags & htole32(WPI_RXON_24GHZ)) == 0;
37344db7dd1bSJoe Talbott
37357370bffcSMatthew Dillon /* Find the TX power group to which this channel belongs. */
37367370bffcSMatthew Dillon if (is_chan_5ghz) {
37374db7dd1bSJoe Talbott for (group = &sc->groups[1]; group < &sc->groups[4]; group++)
37384db7dd1bSJoe Talbott if (chan <= group->chan)
37394db7dd1bSJoe Talbott break;
37404db7dd1bSJoe Talbott } else
37414db7dd1bSJoe Talbott group = &sc->groups[0];
37424db7dd1bSJoe Talbott
37437370bffcSMatthew Dillon memset(&cmd, 0, sizeof cmd);
37447370bffcSMatthew Dillon cmd.band = is_chan_5ghz ? WPI_BAND_5GHZ : WPI_BAND_2GHZ;
37457370bffcSMatthew Dillon cmd.chan = htole16(chan);
37464db7dd1bSJoe Talbott
37477370bffcSMatthew Dillon /* Set TX power for all OFDM and CCK rates. */
37487370bffcSMatthew Dillon for (i = 0; i <= WPI_RIDX_MAX ; i++) {
37497370bffcSMatthew Dillon /* Retrieve TX power for this channel/rate. */
37507370bffcSMatthew Dillon idx = wpi_get_power_index(sc, group, chan, is_chan_5ghz, i);
37514db7dd1bSJoe Talbott
37527370bffcSMatthew Dillon cmd.rates[i].plcp = wpi_ridx_to_plcp[i];
37534db7dd1bSJoe Talbott
37547370bffcSMatthew Dillon if (is_chan_5ghz) {
37557370bffcSMatthew Dillon cmd.rates[i].rf_gain = wpi_rf_gain_5ghz[idx];
37567370bffcSMatthew Dillon cmd.rates[i].dsp_gain = wpi_dsp_gain_5ghz[idx];
37574db7dd1bSJoe Talbott } else {
37587370bffcSMatthew Dillon cmd.rates[i].rf_gain = wpi_rf_gain_2ghz[idx];
37597370bffcSMatthew Dillon cmd.rates[i].dsp_gain = wpi_dsp_gain_2ghz[idx];
37604db7dd1bSJoe Talbott }
37617370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TEMP,
37627370bffcSMatthew Dillon "chan %d/ridx %d: power index %d\n", chan, i, idx);
37634db7dd1bSJoe Talbott }
37644db7dd1bSJoe Talbott
37657370bffcSMatthew Dillon return wpi_cmd(sc, WPI_CMD_TXPOWER, &cmd, sizeof cmd, async);
37664db7dd1bSJoe Talbott }
37674db7dd1bSJoe Talbott
37684db7dd1bSJoe Talbott /*
37694db7dd1bSJoe Talbott * Determine Tx power index for a given channel/rate combination.
37704db7dd1bSJoe Talbott * This takes into account the regulatory information from EEPROM and the
37714db7dd1bSJoe Talbott * current temperature.
37724db7dd1bSJoe Talbott */
37734db7dd1bSJoe Talbott static int
wpi_get_power_index(struct wpi_softc * sc,struct wpi_power_group * group,uint8_t chan,int is_chan_5ghz,int ridx)37744db7dd1bSJoe Talbott wpi_get_power_index(struct wpi_softc *sc, struct wpi_power_group *group,
37757370bffcSMatthew Dillon uint8_t chan, int is_chan_5ghz, int ridx)
37764db7dd1bSJoe Talbott {
37777370bffcSMatthew Dillon /* Fixed-point arithmetic division using a n-bit fractional part. */
37784db7dd1bSJoe Talbott #define fdivround(a, b, n) \
37794db7dd1bSJoe Talbott ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n))
37804db7dd1bSJoe Talbott
37817370bffcSMatthew Dillon /* Linear interpolation. */
37824db7dd1bSJoe Talbott #define interpolate(x, x1, y1, x2, y2, n) \
37834db7dd1bSJoe Talbott ((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
37844db7dd1bSJoe Talbott
37854db7dd1bSJoe Talbott struct wpi_power_sample *sample;
37864db7dd1bSJoe Talbott int pwr, idx;
37874db7dd1bSJoe Talbott
37887370bffcSMatthew Dillon /* Default TX power is group maximum TX power minus 3dB. */
37894db7dd1bSJoe Talbott pwr = group->maxpwr / 2;
37904db7dd1bSJoe Talbott
37917370bffcSMatthew Dillon /* Decrease TX power for highest OFDM rates to reduce distortion. */
37927370bffcSMatthew Dillon switch (ridx) {
37937370bffcSMatthew Dillon case WPI_RIDX_OFDM36:
37947370bffcSMatthew Dillon pwr -= is_chan_5ghz ? 5 : 0;
37954db7dd1bSJoe Talbott break;
37967370bffcSMatthew Dillon case WPI_RIDX_OFDM48:
37977370bffcSMatthew Dillon pwr -= is_chan_5ghz ? 10 : 7;
37984db7dd1bSJoe Talbott break;
37997370bffcSMatthew Dillon case WPI_RIDX_OFDM54:
38007370bffcSMatthew Dillon pwr -= is_chan_5ghz ? 12 : 9;
38014db7dd1bSJoe Talbott break;
38024db7dd1bSJoe Talbott }
38034db7dd1bSJoe Talbott
38047370bffcSMatthew Dillon /* Never exceed the channel maximum allowed TX power. */
38054db7dd1bSJoe Talbott pwr = min(pwr, sc->maxpwr[chan]);
38064db7dd1bSJoe Talbott
38077370bffcSMatthew Dillon /* Retrieve TX power index into gain tables from samples. */
38084db7dd1bSJoe Talbott for (sample = group->samples; sample < &group->samples[3]; sample++)
38094db7dd1bSJoe Talbott if (pwr > sample[1].power)
38104db7dd1bSJoe Talbott break;
38117370bffcSMatthew Dillon /* Fixed-point linear interpolation using a 19-bit fractional part. */
38124db7dd1bSJoe Talbott idx = interpolate(pwr, sample[0].power, sample[0].index,
38134db7dd1bSJoe Talbott sample[1].power, sample[1].index, 19);
38144db7dd1bSJoe Talbott
38157370bffcSMatthew Dillon /*-
38167370bffcSMatthew Dillon * Adjust power index based on current temperature:
38177370bffcSMatthew Dillon * - if cooler than factory-calibrated: decrease output power
38184db7dd1bSJoe Talbott * - if warmer than factory-calibrated: increase output power
38194db7dd1bSJoe Talbott */
38204db7dd1bSJoe Talbott idx -= (sc->temp - group->temp) * 11 / 100;
38214db7dd1bSJoe Talbott
38227370bffcSMatthew Dillon /* Decrease TX power for CCK rates (-5dB). */
38237370bffcSMatthew Dillon if (ridx >= WPI_RIDX_CCK1)
38244db7dd1bSJoe Talbott idx += 10;
38254db7dd1bSJoe Talbott
38267370bffcSMatthew Dillon /* Make sure idx stays in a valid range. */
38274db7dd1bSJoe Talbott if (idx < 0)
38284db7dd1bSJoe Talbott return 0;
38294db7dd1bSJoe Talbott if (idx > WPI_MAX_PWR_INDEX)
38304db7dd1bSJoe Talbott return WPI_MAX_PWR_INDEX;
38314db7dd1bSJoe Talbott return idx;
38324db7dd1bSJoe Talbott
38334db7dd1bSJoe Talbott #undef interpolate
38344db7dd1bSJoe Talbott #undef fdivround
38354db7dd1bSJoe Talbott }
38364db7dd1bSJoe Talbott
38377370bffcSMatthew Dillon /*
38387370bffcSMatthew Dillon * Set STA mode power saving level (between 0 and 5).
38397370bffcSMatthew Dillon * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving.
38407370bffcSMatthew Dillon */
38417370bffcSMatthew Dillon static int
wpi_set_pslevel(struct wpi_softc * sc,uint8_t dtim,int level,int async)38427370bffcSMatthew Dillon wpi_set_pslevel(struct wpi_softc *sc, uint8_t dtim, int level, int async)
38437370bffcSMatthew Dillon {
38447370bffcSMatthew Dillon struct wpi_pmgt_cmd cmd;
38457370bffcSMatthew Dillon const struct wpi_pmgt *pmgt;
38467370bffcSMatthew Dillon uint32_t max, reg;
38477370bffcSMatthew Dillon uint8_t skip_dtim;
38487370bffcSMatthew Dillon int i;
38497370bffcSMatthew Dillon
38507370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_PWRSAVE,
38517370bffcSMatthew Dillon "%s: dtim=%d, level=%d, async=%d\n",
38527370bffcSMatthew Dillon __func__, dtim, level, async);
38537370bffcSMatthew Dillon
38547370bffcSMatthew Dillon /* Select which PS parameters to use. */
38557370bffcSMatthew Dillon if (dtim <= 10)
38567370bffcSMatthew Dillon pmgt = &wpi_pmgt[0][level];
38577370bffcSMatthew Dillon else
38587370bffcSMatthew Dillon pmgt = &wpi_pmgt[1][level];
38597370bffcSMatthew Dillon
38607370bffcSMatthew Dillon memset(&cmd, 0, sizeof cmd);
38617370bffcSMatthew Dillon if (level != 0) /* not CAM */
38627370bffcSMatthew Dillon cmd.flags |= htole16(WPI_PS_ALLOW_SLEEP);
38637370bffcSMatthew Dillon /* Retrieve PCIe Active State Power Management (ASPM). */
38647370bffcSMatthew Dillon #if defined(__DragonFly__)
38657370bffcSMatthew Dillon reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINKCTRL, 1);
38667370bffcSMatthew Dillon if (!(reg & PCIEM_LNKCTL_ASPM_L0S)) /* L0s Entry disabled. */
38677370bffcSMatthew Dillon cmd.flags |= htole16(WPI_PS_PCI_PMGT);
38687370bffcSMatthew Dillon #else
38697370bffcSMatthew Dillon reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, 1);
38707370bffcSMatthew Dillon if (!(reg & PCIEM_LINK_CTL_ASPMC_L0S)) /* L0s Entry disabled. */
38717370bffcSMatthew Dillon cmd.flags |= htole16(WPI_PS_PCI_PMGT);
38727370bffcSMatthew Dillon #endif
38737370bffcSMatthew Dillon
38747370bffcSMatthew Dillon cmd.rxtimeout = htole32(pmgt->rxtimeout * IEEE80211_DUR_TU);
38757370bffcSMatthew Dillon cmd.txtimeout = htole32(pmgt->txtimeout * IEEE80211_DUR_TU);
38767370bffcSMatthew Dillon
38777370bffcSMatthew Dillon if (dtim == 0) {
38787370bffcSMatthew Dillon dtim = 1;
38797370bffcSMatthew Dillon skip_dtim = 0;
38807370bffcSMatthew Dillon } else
38817370bffcSMatthew Dillon skip_dtim = pmgt->skip_dtim;
38827370bffcSMatthew Dillon
38837370bffcSMatthew Dillon if (skip_dtim != 0) {
38847370bffcSMatthew Dillon cmd.flags |= htole16(WPI_PS_SLEEP_OVER_DTIM);
38857370bffcSMatthew Dillon max = pmgt->intval[4];
38867370bffcSMatthew Dillon if (max == (uint32_t)-1)
38877370bffcSMatthew Dillon max = dtim * (skip_dtim + 1);
38887370bffcSMatthew Dillon else if (max > dtim)
38897370bffcSMatthew Dillon max = rounddown(max, dtim);
38907370bffcSMatthew Dillon } else
38917370bffcSMatthew Dillon max = dtim;
38927370bffcSMatthew Dillon
38937370bffcSMatthew Dillon for (i = 0; i < 5; i++)
38947370bffcSMatthew Dillon cmd.intval[i] = htole32(MIN(max, pmgt->intval[i]));
38957370bffcSMatthew Dillon
38967370bffcSMatthew Dillon return wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async);
38977370bffcSMatthew Dillon }
38987370bffcSMatthew Dillon
38997370bffcSMatthew Dillon static int
wpi_send_btcoex(struct wpi_softc * sc)39007370bffcSMatthew Dillon wpi_send_btcoex(struct wpi_softc *sc)
39017370bffcSMatthew Dillon {
39027370bffcSMatthew Dillon struct wpi_bluetooth cmd;
39037370bffcSMatthew Dillon
39047370bffcSMatthew Dillon memset(&cmd, 0, sizeof cmd);
39057370bffcSMatthew Dillon cmd.flags = WPI_BT_COEX_MODE_4WIRE;
39067370bffcSMatthew Dillon cmd.lead_time = WPI_BT_LEAD_TIME_DEF;
39077370bffcSMatthew Dillon cmd.max_kill = WPI_BT_MAX_KILL_DEF;
39087370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_RESET, "%s: configuring bluetooth coexistence\n",
39097370bffcSMatthew Dillon __func__);
39107370bffcSMatthew Dillon return wpi_cmd(sc, WPI_CMD_BT_COEX, &cmd, sizeof(cmd), 0);
39117370bffcSMatthew Dillon }
39127370bffcSMatthew Dillon
39137370bffcSMatthew Dillon static int
wpi_send_rxon(struct wpi_softc * sc,int assoc,int async)39147370bffcSMatthew Dillon wpi_send_rxon(struct wpi_softc *sc, int assoc, int async)
39157370bffcSMatthew Dillon {
39167370bffcSMatthew Dillon int error;
39177370bffcSMatthew Dillon
39187370bffcSMatthew Dillon if (async)
39197370bffcSMatthew Dillon WPI_RXON_LOCK_ASSERT(sc);
39207370bffcSMatthew Dillon
39217370bffcSMatthew Dillon if (assoc && wpi_check_bss_filter(sc) != 0) {
39227370bffcSMatthew Dillon struct wpi_assoc rxon_assoc;
39237370bffcSMatthew Dillon
39247370bffcSMatthew Dillon rxon_assoc.flags = sc->rxon.flags;
39257370bffcSMatthew Dillon rxon_assoc.filter = sc->rxon.filter;
39267370bffcSMatthew Dillon rxon_assoc.ofdm_mask = sc->rxon.ofdm_mask;
39277370bffcSMatthew Dillon rxon_assoc.cck_mask = sc->rxon.cck_mask;
39287370bffcSMatthew Dillon rxon_assoc.reserved = 0;
39297370bffcSMatthew Dillon
39307370bffcSMatthew Dillon error = wpi_cmd(sc, WPI_CMD_RXON_ASSOC, &rxon_assoc,
39317370bffcSMatthew Dillon sizeof (struct wpi_assoc), async);
39327370bffcSMatthew Dillon if (error != 0) {
39337370bffcSMatthew Dillon device_printf(sc->sc_dev,
39347370bffcSMatthew Dillon "RXON_ASSOC command failed, error %d\n", error);
39357370bffcSMatthew Dillon return error;
39367370bffcSMatthew Dillon }
39377370bffcSMatthew Dillon } else {
39387370bffcSMatthew Dillon if (async) {
39397370bffcSMatthew Dillon WPI_NT_LOCK(sc);
39407370bffcSMatthew Dillon error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon,
39417370bffcSMatthew Dillon sizeof (struct wpi_rxon), async);
39427370bffcSMatthew Dillon if (error == 0)
39437370bffcSMatthew Dillon wpi_clear_node_table(sc);
39447370bffcSMatthew Dillon WPI_NT_UNLOCK(sc);
39457370bffcSMatthew Dillon } else {
39467370bffcSMatthew Dillon error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon,
39477370bffcSMatthew Dillon sizeof (struct wpi_rxon), async);
39487370bffcSMatthew Dillon if (error == 0)
39497370bffcSMatthew Dillon wpi_clear_node_table(sc);
39507370bffcSMatthew Dillon }
39517370bffcSMatthew Dillon
39527370bffcSMatthew Dillon if (error != 0) {
39537370bffcSMatthew Dillon device_printf(sc->sc_dev,
39547370bffcSMatthew Dillon "RXON command failed, error %d\n", error);
39557370bffcSMatthew Dillon return error;
39567370bffcSMatthew Dillon }
39577370bffcSMatthew Dillon
39587370bffcSMatthew Dillon /* Add broadcast node. */
39597370bffcSMatthew Dillon error = wpi_add_broadcast_node(sc, async);
39607370bffcSMatthew Dillon if (error != 0) {
39617370bffcSMatthew Dillon device_printf(sc->sc_dev,
39627370bffcSMatthew Dillon "could not add broadcast node, error %d\n", error);
39637370bffcSMatthew Dillon return error;
39647370bffcSMatthew Dillon }
39657370bffcSMatthew Dillon }
39667370bffcSMatthew Dillon
39677370bffcSMatthew Dillon /* Configuration has changed, set Tx power accordingly. */
39687370bffcSMatthew Dillon if ((error = wpi_set_txpower(sc, async)) != 0) {
39697370bffcSMatthew Dillon device_printf(sc->sc_dev,
39707370bffcSMatthew Dillon "%s: could not set TX power, error %d\n", __func__, error);
39717370bffcSMatthew Dillon return error;
39727370bffcSMatthew Dillon }
39737370bffcSMatthew Dillon
39747370bffcSMatthew Dillon return 0;
39757370bffcSMatthew Dillon }
39767370bffcSMatthew Dillon
39774db7dd1bSJoe Talbott /**
39787370bffcSMatthew Dillon * Configure the card to listen to a particular channel, this transisions the
39797370bffcSMatthew Dillon * card in to being able to receive frames from remote devices.
39807370bffcSMatthew Dillon */
39817370bffcSMatthew Dillon static int
wpi_config(struct wpi_softc * sc)39827370bffcSMatthew Dillon wpi_config(struct wpi_softc *sc)
39837370bffcSMatthew Dillon {
39847370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
39857370bffcSMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
39867370bffcSMatthew Dillon struct ieee80211_channel *c = ic->ic_curchan;
39877370bffcSMatthew Dillon int error;
39887370bffcSMatthew Dillon
39897370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
39907370bffcSMatthew Dillon
39917370bffcSMatthew Dillon /* Set power saving level to CAM during initialization. */
39927370bffcSMatthew Dillon if ((error = wpi_set_pslevel(sc, 0, 0, 0)) != 0) {
39937370bffcSMatthew Dillon device_printf(sc->sc_dev,
39947370bffcSMatthew Dillon "%s: could not set power saving level\n", __func__);
39957370bffcSMatthew Dillon return error;
39967370bffcSMatthew Dillon }
39977370bffcSMatthew Dillon
39987370bffcSMatthew Dillon /* Configure bluetooth coexistence. */
39997370bffcSMatthew Dillon if ((error = wpi_send_btcoex(sc)) != 0) {
40007370bffcSMatthew Dillon device_printf(sc->sc_dev,
40017370bffcSMatthew Dillon "could not configure bluetooth coexistence\n");
40027370bffcSMatthew Dillon return error;
40037370bffcSMatthew Dillon }
40047370bffcSMatthew Dillon
40057370bffcSMatthew Dillon /* Configure adapter. */
40067370bffcSMatthew Dillon memset(&sc->rxon, 0, sizeof (struct wpi_rxon));
40077370bffcSMatthew Dillon IEEE80211_ADDR_COPY(sc->rxon.myaddr, vap->iv_myaddr);
40087370bffcSMatthew Dillon
40097370bffcSMatthew Dillon /* Set default channel. */
40107370bffcSMatthew Dillon sc->rxon.chan = ieee80211_chan2ieee(ic, c);
40117370bffcSMatthew Dillon sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF);
40127370bffcSMatthew Dillon if (IEEE80211_IS_CHAN_2GHZ(c))
40137370bffcSMatthew Dillon sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ);
40147370bffcSMatthew Dillon
40157370bffcSMatthew Dillon sc->rxon.filter = WPI_FILTER_MULTICAST;
40167370bffcSMatthew Dillon switch (ic->ic_opmode) {
40177370bffcSMatthew Dillon case IEEE80211_M_STA:
40187370bffcSMatthew Dillon sc->rxon.mode = WPI_MODE_STA;
40197370bffcSMatthew Dillon break;
40207370bffcSMatthew Dillon case IEEE80211_M_IBSS:
40217370bffcSMatthew Dillon sc->rxon.mode = WPI_MODE_IBSS;
40227370bffcSMatthew Dillon sc->rxon.filter |= WPI_FILTER_BEACON;
40237370bffcSMatthew Dillon break;
40247370bffcSMatthew Dillon case IEEE80211_M_HOSTAP:
40257370bffcSMatthew Dillon /* XXX workaround for beaconing */
40267370bffcSMatthew Dillon sc->rxon.mode = WPI_MODE_IBSS;
40277370bffcSMatthew Dillon sc->rxon.filter |= WPI_FILTER_ASSOC | WPI_FILTER_PROMISC;
40287370bffcSMatthew Dillon break;
40297370bffcSMatthew Dillon case IEEE80211_M_AHDEMO:
40307370bffcSMatthew Dillon sc->rxon.mode = WPI_MODE_HOSTAP;
40317370bffcSMatthew Dillon break;
40327370bffcSMatthew Dillon case IEEE80211_M_MONITOR:
40337370bffcSMatthew Dillon sc->rxon.mode = WPI_MODE_MONITOR;
40347370bffcSMatthew Dillon break;
40357370bffcSMatthew Dillon default:
40367370bffcSMatthew Dillon device_printf(sc->sc_dev, "unknown opmode %d\n",
40377370bffcSMatthew Dillon ic->ic_opmode);
40387370bffcSMatthew Dillon return EINVAL;
40397370bffcSMatthew Dillon }
40407370bffcSMatthew Dillon sc->rxon.filter = htole32(sc->rxon.filter);
40417370bffcSMatthew Dillon wpi_set_promisc(sc);
40427370bffcSMatthew Dillon sc->rxon.cck_mask = 0x0f; /* not yet negotiated */
40437370bffcSMatthew Dillon sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */
40447370bffcSMatthew Dillon
40457370bffcSMatthew Dillon if ((error = wpi_send_rxon(sc, 0, 0)) != 0) {
40467370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: could not send RXON\n",
40477370bffcSMatthew Dillon __func__);
40487370bffcSMatthew Dillon return error;
40497370bffcSMatthew Dillon }
40507370bffcSMatthew Dillon
40517370bffcSMatthew Dillon /* Setup rate scalling. */
40527370bffcSMatthew Dillon if ((error = wpi_mrr_setup(sc)) != 0) {
40537370bffcSMatthew Dillon device_printf(sc->sc_dev, "could not setup MRR, error %d\n",
40547370bffcSMatthew Dillon error);
40557370bffcSMatthew Dillon return error;
40567370bffcSMatthew Dillon }
40577370bffcSMatthew Dillon
40587370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
40597370bffcSMatthew Dillon
40607370bffcSMatthew Dillon return 0;
40617370bffcSMatthew Dillon }
40627370bffcSMatthew Dillon
40637370bffcSMatthew Dillon static uint16_t
wpi_get_active_dwell_time(struct wpi_softc * sc,struct ieee80211_channel * c,uint8_t n_probes)40647370bffcSMatthew Dillon wpi_get_active_dwell_time(struct wpi_softc *sc,
40657370bffcSMatthew Dillon struct ieee80211_channel *c, uint8_t n_probes)
40667370bffcSMatthew Dillon {
40677370bffcSMatthew Dillon /* No channel? Default to 2GHz settings. */
40687370bffcSMatthew Dillon if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) {
40697370bffcSMatthew Dillon return (WPI_ACTIVE_DWELL_TIME_2GHZ +
40707370bffcSMatthew Dillon WPI_ACTIVE_DWELL_FACTOR_2GHZ * (n_probes + 1));
40717370bffcSMatthew Dillon }
40727370bffcSMatthew Dillon
40737370bffcSMatthew Dillon /* 5GHz dwell time. */
40747370bffcSMatthew Dillon return (WPI_ACTIVE_DWELL_TIME_5GHZ +
40757370bffcSMatthew Dillon WPI_ACTIVE_DWELL_FACTOR_5GHZ * (n_probes + 1));
40767370bffcSMatthew Dillon }
40777370bffcSMatthew Dillon
40787370bffcSMatthew Dillon /*
40797370bffcSMatthew Dillon * Limit the total dwell time.
40807370bffcSMatthew Dillon *
40817370bffcSMatthew Dillon * Returns the dwell time in milliseconds.
40827370bffcSMatthew Dillon */
40837370bffcSMatthew Dillon static uint16_t
wpi_limit_dwell(struct wpi_softc * sc,uint16_t dwell_time)40847370bffcSMatthew Dillon wpi_limit_dwell(struct wpi_softc *sc, uint16_t dwell_time)
40857370bffcSMatthew Dillon {
40867370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
40877370bffcSMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
40887370bffcSMatthew Dillon uint16_t bintval = 0;
40897370bffcSMatthew Dillon
40907370bffcSMatthew Dillon /* bintval is in TU (1.024mS) */
40917370bffcSMatthew Dillon if (vap != NULL)
40927370bffcSMatthew Dillon bintval = vap->iv_bss->ni_intval;
40937370bffcSMatthew Dillon
40947370bffcSMatthew Dillon /*
40957370bffcSMatthew Dillon * If it's non-zero, we should calculate the minimum of
40967370bffcSMatthew Dillon * it and the DWELL_BASE.
40977370bffcSMatthew Dillon *
40987370bffcSMatthew Dillon * XXX Yes, the math should take into account that bintval
40997370bffcSMatthew Dillon * is 1.024mS, not 1mS..
41007370bffcSMatthew Dillon */
41017370bffcSMatthew Dillon if (bintval > 0) {
41027370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_SCAN, "%s: bintval=%d\n", __func__,
41037370bffcSMatthew Dillon bintval);
41047370bffcSMatthew Dillon return (MIN(dwell_time, bintval - WPI_CHANNEL_TUNE_TIME * 2));
41057370bffcSMatthew Dillon }
41067370bffcSMatthew Dillon
41077370bffcSMatthew Dillon /* No association context? Default. */
41087370bffcSMatthew Dillon return dwell_time;
41097370bffcSMatthew Dillon }
41107370bffcSMatthew Dillon
41117370bffcSMatthew Dillon static uint16_t
wpi_get_passive_dwell_time(struct wpi_softc * sc,struct ieee80211_channel * c)41127370bffcSMatthew Dillon wpi_get_passive_dwell_time(struct wpi_softc *sc, struct ieee80211_channel *c)
41137370bffcSMatthew Dillon {
41147370bffcSMatthew Dillon uint16_t passive;
41157370bffcSMatthew Dillon
41167370bffcSMatthew Dillon if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c))
41177370bffcSMatthew Dillon passive = WPI_PASSIVE_DWELL_BASE + WPI_PASSIVE_DWELL_TIME_2GHZ;
41187370bffcSMatthew Dillon else
41197370bffcSMatthew Dillon passive = WPI_PASSIVE_DWELL_BASE + WPI_PASSIVE_DWELL_TIME_5GHZ;
41207370bffcSMatthew Dillon
41217370bffcSMatthew Dillon /* Clamp to the beacon interval if we're associated. */
41227370bffcSMatthew Dillon return (wpi_limit_dwell(sc, passive));
41237370bffcSMatthew Dillon }
41247370bffcSMatthew Dillon
41257370bffcSMatthew Dillon static uint32_t
wpi_get_scan_pause_time(uint32_t time,uint16_t bintval)41267370bffcSMatthew Dillon wpi_get_scan_pause_time(uint32_t time, uint16_t bintval)
41277370bffcSMatthew Dillon {
41287370bffcSMatthew Dillon uint32_t mod = (time % bintval) * IEEE80211_DUR_TU;
41297370bffcSMatthew Dillon uint32_t nbeacons = time / bintval;
41307370bffcSMatthew Dillon
41317370bffcSMatthew Dillon if (mod > WPI_PAUSE_MAX_TIME)
41327370bffcSMatthew Dillon mod = WPI_PAUSE_MAX_TIME;
41337370bffcSMatthew Dillon
41347370bffcSMatthew Dillon return WPI_PAUSE_SCAN(nbeacons, mod);
41357370bffcSMatthew Dillon }
41367370bffcSMatthew Dillon
41377370bffcSMatthew Dillon /*
41387370bffcSMatthew Dillon * Send a scan request to the firmware.
41397370bffcSMatthew Dillon */
41407370bffcSMatthew Dillon static int
wpi_scan(struct wpi_softc * sc,struct ieee80211_channel * c)41417370bffcSMatthew Dillon wpi_scan(struct wpi_softc *sc, struct ieee80211_channel *c)
41427370bffcSMatthew Dillon {
41437370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
41447370bffcSMatthew Dillon struct ieee80211_scan_state *ss = ic->ic_scan;
41457370bffcSMatthew Dillon struct ieee80211vap *vap = ss->ss_vap;
41467370bffcSMatthew Dillon struct wpi_scan_hdr *hdr;
41477370bffcSMatthew Dillon struct wpi_cmd_data *tx;
41487370bffcSMatthew Dillon struct wpi_scan_essid *essids;
41497370bffcSMatthew Dillon struct wpi_scan_chan *chan;
41507370bffcSMatthew Dillon struct ieee80211_frame *wh;
41517370bffcSMatthew Dillon struct ieee80211_rateset *rs;
41527370bffcSMatthew Dillon uint16_t bintval, buflen, dwell_active, dwell_passive;
41537370bffcSMatthew Dillon uint8_t *buf, *frm, i, nssid;
41547370bffcSMatthew Dillon int bgscan, error;
41557370bffcSMatthew Dillon
41567370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
41577370bffcSMatthew Dillon
41587370bffcSMatthew Dillon /*
41597370bffcSMatthew Dillon * We are absolutely not allowed to send a scan command when another
41607370bffcSMatthew Dillon * scan command is pending.
41617370bffcSMatthew Dillon */
41627370bffcSMatthew Dillon if (callout_pending(&sc->scan_timeout)) {
41637370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: called whilst scanning!\n",
41647370bffcSMatthew Dillon __func__);
41657370bffcSMatthew Dillon error = EAGAIN;
41667370bffcSMatthew Dillon goto fail;
41677370bffcSMatthew Dillon }
41687370bffcSMatthew Dillon
41697370bffcSMatthew Dillon bgscan = wpi_check_bss_filter(sc);
41707370bffcSMatthew Dillon bintval = vap->iv_bss->ni_intval;
41717370bffcSMatthew Dillon if (bgscan != 0 &&
41727370bffcSMatthew Dillon bintval < WPI_QUIET_TIME_DEFAULT + WPI_CHANNEL_TUNE_TIME * 2) {
41737370bffcSMatthew Dillon error = EOPNOTSUPP;
41747370bffcSMatthew Dillon goto fail;
41757370bffcSMatthew Dillon }
41767370bffcSMatthew Dillon
41777370bffcSMatthew Dillon buf = kmalloc(WPI_SCAN_MAXSZ, M_DEVBUF, M_INTWAIT | M_ZERO);
41787370bffcSMatthew Dillon if (buf == NULL) {
41797370bffcSMatthew Dillon device_printf(sc->sc_dev,
41807370bffcSMatthew Dillon "%s: could not allocate buffer for scan command\n",
41817370bffcSMatthew Dillon __func__);
41827370bffcSMatthew Dillon error = ENOMEM;
41837370bffcSMatthew Dillon goto fail;
41847370bffcSMatthew Dillon }
41857370bffcSMatthew Dillon hdr = (struct wpi_scan_hdr *)buf;
41867370bffcSMatthew Dillon
41877370bffcSMatthew Dillon /*
41887370bffcSMatthew Dillon * Move to the next channel if no packets are received within 10 msecs
41897370bffcSMatthew Dillon * after sending the probe request.
41907370bffcSMatthew Dillon */
41917370bffcSMatthew Dillon hdr->quiet_time = htole16(WPI_QUIET_TIME_DEFAULT);
41927370bffcSMatthew Dillon hdr->quiet_threshold = htole16(1);
41937370bffcSMatthew Dillon
41947370bffcSMatthew Dillon if (bgscan != 0) {
41957370bffcSMatthew Dillon /*
41967370bffcSMatthew Dillon * Max needs to be greater than active and passive and quiet!
41977370bffcSMatthew Dillon * It's also in microseconds!
41987370bffcSMatthew Dillon */
41997370bffcSMatthew Dillon hdr->max_svc = htole32(250 * IEEE80211_DUR_TU);
42007370bffcSMatthew Dillon hdr->pause_svc = htole32(wpi_get_scan_pause_time(100,
42017370bffcSMatthew Dillon bintval));
42027370bffcSMatthew Dillon }
42037370bffcSMatthew Dillon
42047370bffcSMatthew Dillon hdr->filter = htole32(WPI_FILTER_MULTICAST | WPI_FILTER_BEACON);
42057370bffcSMatthew Dillon
42067370bffcSMatthew Dillon tx = (struct wpi_cmd_data *)(hdr + 1);
42077370bffcSMatthew Dillon tx->flags = htole32(WPI_TX_AUTO_SEQ);
42087370bffcSMatthew Dillon tx->id = WPI_ID_BROADCAST;
42097370bffcSMatthew Dillon tx->lifetime = htole32(WPI_LIFETIME_INFINITE);
42107370bffcSMatthew Dillon
42117370bffcSMatthew Dillon if (IEEE80211_IS_CHAN_5GHZ(c)) {
42127370bffcSMatthew Dillon /* Send probe requests at 6Mbps. */
42137370bffcSMatthew Dillon tx->plcp = wpi_ridx_to_plcp[WPI_RIDX_OFDM6];
42147370bffcSMatthew Dillon rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
42157370bffcSMatthew Dillon } else {
42167370bffcSMatthew Dillon hdr->flags = htole32(WPI_RXON_24GHZ | WPI_RXON_AUTO);
42177370bffcSMatthew Dillon /* Send probe requests at 1Mbps. */
42187370bffcSMatthew Dillon tx->plcp = wpi_ridx_to_plcp[WPI_RIDX_CCK1];
42197370bffcSMatthew Dillon rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
42207370bffcSMatthew Dillon }
42217370bffcSMatthew Dillon
42227370bffcSMatthew Dillon essids = (struct wpi_scan_essid *)(tx + 1);
42237370bffcSMatthew Dillon nssid = MIN(ss->ss_nssid, WPI_SCAN_MAX_ESSIDS);
42247370bffcSMatthew Dillon for (i = 0; i < nssid; i++) {
42257370bffcSMatthew Dillon essids[i].id = IEEE80211_ELEMID_SSID;
42267370bffcSMatthew Dillon essids[i].len = MIN(ss->ss_ssid[i].len, IEEE80211_NWID_LEN);
42277370bffcSMatthew Dillon memcpy(essids[i].data, ss->ss_ssid[i].ssid, essids[i].len);
42287370bffcSMatthew Dillon #ifdef WPI_DEBUG
42297370bffcSMatthew Dillon if (sc->sc_debug & WPI_DEBUG_SCAN) {
4230e4bea863SSascha Wildner kprintf("Scanning Essid: ");
42317370bffcSMatthew Dillon ieee80211_print_essid(essids[i].data, essids[i].len);
4232e4bea863SSascha Wildner kprintf("\n");
42337370bffcSMatthew Dillon }
42347370bffcSMatthew Dillon #endif
42357370bffcSMatthew Dillon }
42367370bffcSMatthew Dillon
42377370bffcSMatthew Dillon /*
42387370bffcSMatthew Dillon * Build a probe request frame. Most of the following code is a
42397370bffcSMatthew Dillon * copy & paste of what is done in net80211.
42407370bffcSMatthew Dillon */
42417370bffcSMatthew Dillon wh = (struct ieee80211_frame *)(essids + WPI_SCAN_MAX_ESSIDS);
42427370bffcSMatthew Dillon wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
42437370bffcSMatthew Dillon IEEE80211_FC0_SUBTYPE_PROBE_REQ;
42447370bffcSMatthew Dillon wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
42457370bffcSMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr);
42467370bffcSMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
42477370bffcSMatthew Dillon IEEE80211_ADDR_COPY(wh->i_addr3, ieee80211broadcastaddr);
42487370bffcSMatthew Dillon
42497370bffcSMatthew Dillon frm = (uint8_t *)(wh + 1);
42507370bffcSMatthew Dillon frm = ieee80211_add_ssid(frm, NULL, 0);
42517370bffcSMatthew Dillon frm = ieee80211_add_rates(frm, rs);
42527370bffcSMatthew Dillon if (rs->rs_nrates > IEEE80211_RATE_SIZE)
42537370bffcSMatthew Dillon frm = ieee80211_add_xrates(frm, rs);
42547370bffcSMatthew Dillon
42557370bffcSMatthew Dillon /* Set length of probe request. */
42567370bffcSMatthew Dillon tx->len = htole16(frm - (uint8_t *)wh);
42577370bffcSMatthew Dillon
42587370bffcSMatthew Dillon /*
42597370bffcSMatthew Dillon * Construct information about the channel that we
42607370bffcSMatthew Dillon * want to scan. The firmware expects this to be directly
42617370bffcSMatthew Dillon * after the scan probe request
42627370bffcSMatthew Dillon */
42637370bffcSMatthew Dillon chan = (struct wpi_scan_chan *)frm;
42647370bffcSMatthew Dillon chan->chan = ieee80211_chan2ieee(ic, c);
42657370bffcSMatthew Dillon chan->flags = 0;
42667370bffcSMatthew Dillon if (nssid) {
42677370bffcSMatthew Dillon hdr->crc_threshold = WPI_SCAN_CRC_TH_DEFAULT;
42687370bffcSMatthew Dillon chan->flags |= WPI_CHAN_NPBREQS(nssid);
42697370bffcSMatthew Dillon } else
42707370bffcSMatthew Dillon hdr->crc_threshold = WPI_SCAN_CRC_TH_NEVER;
42717370bffcSMatthew Dillon
42727370bffcSMatthew Dillon if (!IEEE80211_IS_CHAN_PASSIVE(c))
42737370bffcSMatthew Dillon chan->flags |= WPI_CHAN_ACTIVE;
42747370bffcSMatthew Dillon
42757370bffcSMatthew Dillon /*
42767370bffcSMatthew Dillon * Calculate the active/passive dwell times.
42777370bffcSMatthew Dillon */
42787370bffcSMatthew Dillon dwell_active = wpi_get_active_dwell_time(sc, c, nssid);
42797370bffcSMatthew Dillon dwell_passive = wpi_get_passive_dwell_time(sc, c);
42807370bffcSMatthew Dillon
42817370bffcSMatthew Dillon /* Make sure they're valid. */
42827370bffcSMatthew Dillon if (dwell_active > dwell_passive)
42837370bffcSMatthew Dillon dwell_active = dwell_passive;
42847370bffcSMatthew Dillon
42857370bffcSMatthew Dillon chan->active = htole16(dwell_active);
42867370bffcSMatthew Dillon chan->passive = htole16(dwell_passive);
42877370bffcSMatthew Dillon
42887370bffcSMatthew Dillon chan->dsp_gain = 0x6e; /* Default level */
42897370bffcSMatthew Dillon
42907370bffcSMatthew Dillon if (IEEE80211_IS_CHAN_5GHZ(c))
42917370bffcSMatthew Dillon chan->rf_gain = 0x3b;
42927370bffcSMatthew Dillon else
42937370bffcSMatthew Dillon chan->rf_gain = 0x28;
42947370bffcSMatthew Dillon
42957370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_SCAN, "Scanning %u Passive: %d\n",
42967370bffcSMatthew Dillon chan->chan, IEEE80211_IS_CHAN_PASSIVE(c));
42977370bffcSMatthew Dillon
42987370bffcSMatthew Dillon hdr->nchan++;
42997370bffcSMatthew Dillon
43007370bffcSMatthew Dillon if (hdr->nchan == 1 && sc->rxon.chan == chan->chan) {
43017370bffcSMatthew Dillon /* XXX Force probe request transmission. */
43027370bffcSMatthew Dillon memcpy(chan + 1, chan, sizeof (struct wpi_scan_chan));
43037370bffcSMatthew Dillon
43047370bffcSMatthew Dillon chan++;
43057370bffcSMatthew Dillon
43067370bffcSMatthew Dillon /* Reduce unnecessary delay. */
43077370bffcSMatthew Dillon chan->flags = 0;
43087370bffcSMatthew Dillon chan->passive = chan->active = hdr->quiet_time;
43097370bffcSMatthew Dillon
43107370bffcSMatthew Dillon hdr->nchan++;
43117370bffcSMatthew Dillon }
43127370bffcSMatthew Dillon
43137370bffcSMatthew Dillon chan++;
43147370bffcSMatthew Dillon
43157370bffcSMatthew Dillon buflen = (uint8_t *)chan - buf;
43167370bffcSMatthew Dillon hdr->len = htole16(buflen);
43177370bffcSMatthew Dillon
43187370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_CMD, "sending scan command nchan=%d\n",
43197370bffcSMatthew Dillon hdr->nchan);
43207370bffcSMatthew Dillon error = wpi_cmd(sc, WPI_CMD_SCAN, buf, buflen, 1);
43217370bffcSMatthew Dillon kfree(buf, M_DEVBUF);
43227370bffcSMatthew Dillon
43237370bffcSMatthew Dillon if (error != 0)
43247370bffcSMatthew Dillon goto fail;
43257370bffcSMatthew Dillon
43267370bffcSMatthew Dillon callout_reset(&sc->scan_timeout, 5*hz, wpi_scan_timeout, sc);
43277370bffcSMatthew Dillon
43287370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
43297370bffcSMatthew Dillon
43307370bffcSMatthew Dillon return 0;
43317370bffcSMatthew Dillon
43327370bffcSMatthew Dillon fail: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__);
43337370bffcSMatthew Dillon
43347370bffcSMatthew Dillon return error;
43357370bffcSMatthew Dillon }
43367370bffcSMatthew Dillon
43377370bffcSMatthew Dillon static int
wpi_auth(struct wpi_softc * sc,struct ieee80211vap * vap)43387370bffcSMatthew Dillon wpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap)
43397370bffcSMatthew Dillon {
43407370bffcSMatthew Dillon struct ieee80211com *ic = vap->iv_ic;
43417370bffcSMatthew Dillon struct ieee80211_node *ni = vap->iv_bss;
43427370bffcSMatthew Dillon struct ieee80211_channel *c = ni->ni_chan;
43437370bffcSMatthew Dillon int error;
43447370bffcSMatthew Dillon
43457370bffcSMatthew Dillon WPI_RXON_LOCK(sc);
43467370bffcSMatthew Dillon
43477370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
43487370bffcSMatthew Dillon
43497370bffcSMatthew Dillon /* Update adapter configuration. */
43507370bffcSMatthew Dillon sc->rxon.associd = 0;
43517370bffcSMatthew Dillon sc->rxon.filter &= ~htole32(WPI_FILTER_BSS);
43527370bffcSMatthew Dillon IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
43537370bffcSMatthew Dillon sc->rxon.chan = ieee80211_chan2ieee(ic, c);
43547370bffcSMatthew Dillon sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF);
43557370bffcSMatthew Dillon if (IEEE80211_IS_CHAN_2GHZ(c))
43567370bffcSMatthew Dillon sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ);
43577370bffcSMatthew Dillon if (ic->ic_flags & IEEE80211_F_SHSLOT)
43587370bffcSMatthew Dillon sc->rxon.flags |= htole32(WPI_RXON_SHSLOT);
43597370bffcSMatthew Dillon if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
43607370bffcSMatthew Dillon sc->rxon.flags |= htole32(WPI_RXON_SHPREAMBLE);
43617370bffcSMatthew Dillon if (IEEE80211_IS_CHAN_A(c)) {
43627370bffcSMatthew Dillon sc->rxon.cck_mask = 0;
43637370bffcSMatthew Dillon sc->rxon.ofdm_mask = 0x15;
43647370bffcSMatthew Dillon } else if (IEEE80211_IS_CHAN_B(c)) {
43657370bffcSMatthew Dillon sc->rxon.cck_mask = 0x03;
43667370bffcSMatthew Dillon sc->rxon.ofdm_mask = 0;
43677370bffcSMatthew Dillon } else {
43687370bffcSMatthew Dillon /* Assume 802.11b/g. */
43697370bffcSMatthew Dillon sc->rxon.cck_mask = 0x0f;
43707370bffcSMatthew Dillon sc->rxon.ofdm_mask = 0x15;
43717370bffcSMatthew Dillon }
43727370bffcSMatthew Dillon
43737370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n",
43747370bffcSMatthew Dillon sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask,
43757370bffcSMatthew Dillon sc->rxon.ofdm_mask);
43767370bffcSMatthew Dillon
43777370bffcSMatthew Dillon if ((error = wpi_send_rxon(sc, 0, 1)) != 0) {
43787370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: could not send RXON\n",
43797370bffcSMatthew Dillon __func__);
43807370bffcSMatthew Dillon }
43817370bffcSMatthew Dillon
43827370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
43837370bffcSMatthew Dillon
43847370bffcSMatthew Dillon WPI_RXON_UNLOCK(sc);
43857370bffcSMatthew Dillon
43867370bffcSMatthew Dillon return error;
43877370bffcSMatthew Dillon }
43887370bffcSMatthew Dillon
43897370bffcSMatthew Dillon static int
wpi_config_beacon(struct wpi_vap * wvp)43907370bffcSMatthew Dillon wpi_config_beacon(struct wpi_vap *wvp)
43917370bffcSMatthew Dillon {
43927370bffcSMatthew Dillon struct ieee80211vap *vap = &wvp->wv_vap;
43937370bffcSMatthew Dillon struct ieee80211com *ic = vap->iv_ic;
43947370bffcSMatthew Dillon struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off;
43957370bffcSMatthew Dillon struct wpi_buf *bcn = &wvp->wv_bcbuf;
43967370bffcSMatthew Dillon struct wpi_softc *sc = ic->ic_softc;
43977370bffcSMatthew Dillon struct wpi_cmd_beacon *cmd = (struct wpi_cmd_beacon *)&bcn->data;
43987370bffcSMatthew Dillon struct ieee80211_tim_ie *tie;
43997370bffcSMatthew Dillon struct mbuf *m;
44007370bffcSMatthew Dillon uint8_t *ptr;
44017370bffcSMatthew Dillon int error;
44027370bffcSMatthew Dillon
44037370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
44047370bffcSMatthew Dillon
44057370bffcSMatthew Dillon WPI_VAP_LOCK_ASSERT(wvp);
44067370bffcSMatthew Dillon
44077370bffcSMatthew Dillon cmd->len = htole16(bcn->m->m_pkthdr.len);
44087370bffcSMatthew Dillon cmd->plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ?
44097370bffcSMatthew Dillon wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1];
44107370bffcSMatthew Dillon
44117370bffcSMatthew Dillon /* XXX seems to be unused */
44127370bffcSMatthew Dillon if (*(bo->bo_tim) == IEEE80211_ELEMID_TIM) {
44137370bffcSMatthew Dillon tie = (struct ieee80211_tim_ie *) bo->bo_tim;
44147370bffcSMatthew Dillon ptr = mtod(bcn->m, uint8_t *);
44157370bffcSMatthew Dillon
44167370bffcSMatthew Dillon cmd->tim = htole16(bo->bo_tim - ptr);
44177370bffcSMatthew Dillon cmd->timsz = tie->tim_len;
44187370bffcSMatthew Dillon }
44197370bffcSMatthew Dillon
44207370bffcSMatthew Dillon /* Necessary for recursion in ieee80211_beacon_update(). */
44217370bffcSMatthew Dillon m = bcn->m;
44227370bffcSMatthew Dillon bcn->m = m_dup(m, M_NOWAIT);
44237370bffcSMatthew Dillon if (bcn->m == NULL) {
44247370bffcSMatthew Dillon device_printf(sc->sc_dev,
44257370bffcSMatthew Dillon "%s: could not copy beacon frame\n", __func__);
44267370bffcSMatthew Dillon error = ENOMEM;
44277370bffcSMatthew Dillon goto end;
44287370bffcSMatthew Dillon }
44297370bffcSMatthew Dillon
44307370bffcSMatthew Dillon if ((error = wpi_cmd2(sc, bcn)) != 0) {
44317370bffcSMatthew Dillon device_printf(sc->sc_dev,
44327370bffcSMatthew Dillon "%s: could not update beacon frame, error %d", __func__,
44337370bffcSMatthew Dillon error);
44347370bffcSMatthew Dillon m_freem(bcn->m);
44357370bffcSMatthew Dillon }
44367370bffcSMatthew Dillon
44377370bffcSMatthew Dillon /* Restore mbuf. */
44387370bffcSMatthew Dillon end: bcn->m = m;
44397370bffcSMatthew Dillon
44407370bffcSMatthew Dillon return error;
44417370bffcSMatthew Dillon }
44427370bffcSMatthew Dillon
44437370bffcSMatthew Dillon static int
wpi_setup_beacon(struct wpi_softc * sc,struct ieee80211_node * ni)44447370bffcSMatthew Dillon wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni)
44457370bffcSMatthew Dillon {
44467370bffcSMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
44477370bffcSMatthew Dillon struct wpi_vap *wvp = WPI_VAP(vap);
44487370bffcSMatthew Dillon struct wpi_buf *bcn = &wvp->wv_bcbuf;
44497370bffcSMatthew Dillon struct mbuf *m;
44507370bffcSMatthew Dillon int error;
44517370bffcSMatthew Dillon
44527370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
44537370bffcSMatthew Dillon
44547370bffcSMatthew Dillon if (ni->ni_chan == IEEE80211_CHAN_ANYC)
44557370bffcSMatthew Dillon return EINVAL;
44567370bffcSMatthew Dillon
44577370bffcSMatthew Dillon m = ieee80211_beacon_alloc(ni);
44587370bffcSMatthew Dillon if (m == NULL) {
44597370bffcSMatthew Dillon device_printf(sc->sc_dev,
44607370bffcSMatthew Dillon "%s: could not allocate beacon frame\n", __func__);
44617370bffcSMatthew Dillon return ENOMEM;
44627370bffcSMatthew Dillon }
44637370bffcSMatthew Dillon
44647370bffcSMatthew Dillon WPI_VAP_LOCK(wvp);
44657370bffcSMatthew Dillon if (bcn->m != NULL)
44667370bffcSMatthew Dillon m_freem(bcn->m);
44677370bffcSMatthew Dillon
44687370bffcSMatthew Dillon bcn->m = m;
44697370bffcSMatthew Dillon
44707370bffcSMatthew Dillon error = wpi_config_beacon(wvp);
44717370bffcSMatthew Dillon WPI_VAP_UNLOCK(wvp);
44727370bffcSMatthew Dillon
44737370bffcSMatthew Dillon return error;
44747370bffcSMatthew Dillon }
44757370bffcSMatthew Dillon
44767370bffcSMatthew Dillon static void
wpi_update_beacon(struct ieee80211vap * vap,int item)44777370bffcSMatthew Dillon wpi_update_beacon(struct ieee80211vap *vap, int item)
44787370bffcSMatthew Dillon {
44797370bffcSMatthew Dillon struct wpi_softc *sc = vap->iv_ic->ic_softc;
44807370bffcSMatthew Dillon struct wpi_vap *wvp = WPI_VAP(vap);
44817370bffcSMatthew Dillon struct wpi_buf *bcn = &wvp->wv_bcbuf;
44827370bffcSMatthew Dillon struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off;
44837370bffcSMatthew Dillon struct ieee80211_node *ni = vap->iv_bss;
44847370bffcSMatthew Dillon int mcast = 0;
44857370bffcSMatthew Dillon
44867370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
44877370bffcSMatthew Dillon
44887370bffcSMatthew Dillon WPI_VAP_LOCK(wvp);
44897370bffcSMatthew Dillon if (bcn->m == NULL) {
44907370bffcSMatthew Dillon bcn->m = ieee80211_beacon_alloc(ni);
44917370bffcSMatthew Dillon if (bcn->m == NULL) {
44927370bffcSMatthew Dillon device_printf(sc->sc_dev,
44937370bffcSMatthew Dillon "%s: could not allocate beacon frame\n", __func__);
44947370bffcSMatthew Dillon
44957370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR,
44967370bffcSMatthew Dillon __func__);
44977370bffcSMatthew Dillon
44987370bffcSMatthew Dillon WPI_VAP_UNLOCK(wvp);
44997370bffcSMatthew Dillon return;
45007370bffcSMatthew Dillon }
45017370bffcSMatthew Dillon }
45027370bffcSMatthew Dillon WPI_VAP_UNLOCK(wvp);
45037370bffcSMatthew Dillon
45047370bffcSMatthew Dillon if (item == IEEE80211_BEACON_TIM)
45057370bffcSMatthew Dillon mcast = 1; /* TODO */
45067370bffcSMatthew Dillon
45077370bffcSMatthew Dillon setbit(bo->bo_flags, item);
45087370bffcSMatthew Dillon ieee80211_beacon_update(ni, bcn->m, mcast);
45097370bffcSMatthew Dillon
45107370bffcSMatthew Dillon WPI_VAP_LOCK(wvp);
45117370bffcSMatthew Dillon wpi_config_beacon(wvp);
45127370bffcSMatthew Dillon WPI_VAP_UNLOCK(wvp);
45137370bffcSMatthew Dillon
45147370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
45157370bffcSMatthew Dillon }
45167370bffcSMatthew Dillon
45177370bffcSMatthew Dillon static void
wpi_newassoc(struct ieee80211_node * ni,int isnew)45187370bffcSMatthew Dillon wpi_newassoc(struct ieee80211_node *ni, int isnew)
45197370bffcSMatthew Dillon {
45207370bffcSMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
45217370bffcSMatthew Dillon struct wpi_softc *sc = ni->ni_ic->ic_softc;
45227370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
45237370bffcSMatthew Dillon int error;
45247370bffcSMatthew Dillon
45257370bffcSMatthew Dillon WPI_NT_LOCK(sc);
45267370bffcSMatthew Dillon
45277370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
45287370bffcSMatthew Dillon
45297370bffcSMatthew Dillon if (vap->iv_opmode != IEEE80211_M_STA && wn->id == WPI_ID_UNDEFINED) {
45307370bffcSMatthew Dillon if ((error = wpi_add_ibss_node(sc, ni)) != 0) {
45317370bffcSMatthew Dillon device_printf(sc->sc_dev,
45327370bffcSMatthew Dillon "%s: could not add IBSS node, error %d\n",
45337370bffcSMatthew Dillon __func__, error);
45347370bffcSMatthew Dillon }
45357370bffcSMatthew Dillon }
45367370bffcSMatthew Dillon WPI_NT_UNLOCK(sc);
45377370bffcSMatthew Dillon }
45387370bffcSMatthew Dillon
45397370bffcSMatthew Dillon static int
wpi_run(struct wpi_softc * sc,struct ieee80211vap * vap)45407370bffcSMatthew Dillon wpi_run(struct wpi_softc *sc, struct ieee80211vap *vap)
45417370bffcSMatthew Dillon {
45427370bffcSMatthew Dillon struct ieee80211com *ic = vap->iv_ic;
45437370bffcSMatthew Dillon struct ieee80211_node *ni = vap->iv_bss;
45447370bffcSMatthew Dillon struct ieee80211_channel *c = ni->ni_chan;
45457370bffcSMatthew Dillon int error;
45467370bffcSMatthew Dillon
45477370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
45487370bffcSMatthew Dillon
45497370bffcSMatthew Dillon if (vap->iv_opmode == IEEE80211_M_MONITOR) {
45507370bffcSMatthew Dillon /* Link LED blinks while monitoring. */
45517370bffcSMatthew Dillon wpi_set_led(sc, WPI_LED_LINK, 5, 5);
45527370bffcSMatthew Dillon return 0;
45537370bffcSMatthew Dillon }
45547370bffcSMatthew Dillon
45557370bffcSMatthew Dillon /* XXX kernel panic workaround */
45567370bffcSMatthew Dillon if (c == IEEE80211_CHAN_ANYC) {
45577370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: incomplete configuration\n",
45587370bffcSMatthew Dillon __func__);
45597370bffcSMatthew Dillon return EINVAL;
45607370bffcSMatthew Dillon }
45617370bffcSMatthew Dillon
45627370bffcSMatthew Dillon if ((error = wpi_set_timing(sc, ni)) != 0) {
45637370bffcSMatthew Dillon device_printf(sc->sc_dev,
45647370bffcSMatthew Dillon "%s: could not set timing, error %d\n", __func__, error);
45657370bffcSMatthew Dillon return error;
45667370bffcSMatthew Dillon }
45677370bffcSMatthew Dillon
45687370bffcSMatthew Dillon /* Update adapter configuration. */
45697370bffcSMatthew Dillon WPI_RXON_LOCK(sc);
45707370bffcSMatthew Dillon IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
45717370bffcSMatthew Dillon sc->rxon.associd = htole16(IEEE80211_NODE_AID(ni));
45727370bffcSMatthew Dillon sc->rxon.chan = ieee80211_chan2ieee(ic, c);
45737370bffcSMatthew Dillon sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF);
45747370bffcSMatthew Dillon if (IEEE80211_IS_CHAN_2GHZ(c))
45757370bffcSMatthew Dillon sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ);
45767370bffcSMatthew Dillon if (ic->ic_flags & IEEE80211_F_SHSLOT)
45777370bffcSMatthew Dillon sc->rxon.flags |= htole32(WPI_RXON_SHSLOT);
45787370bffcSMatthew Dillon if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
45797370bffcSMatthew Dillon sc->rxon.flags |= htole32(WPI_RXON_SHPREAMBLE);
45807370bffcSMatthew Dillon if (IEEE80211_IS_CHAN_A(c)) {
45817370bffcSMatthew Dillon sc->rxon.cck_mask = 0;
45827370bffcSMatthew Dillon sc->rxon.ofdm_mask = 0x15;
45837370bffcSMatthew Dillon } else if (IEEE80211_IS_CHAN_B(c)) {
45847370bffcSMatthew Dillon sc->rxon.cck_mask = 0x03;
45857370bffcSMatthew Dillon sc->rxon.ofdm_mask = 0;
45867370bffcSMatthew Dillon } else {
45877370bffcSMatthew Dillon /* Assume 802.11b/g. */
45887370bffcSMatthew Dillon sc->rxon.cck_mask = 0x0f;
45897370bffcSMatthew Dillon sc->rxon.ofdm_mask = 0x15;
45907370bffcSMatthew Dillon }
45917370bffcSMatthew Dillon sc->rxon.filter |= htole32(WPI_FILTER_BSS);
45927370bffcSMatthew Dillon
45937370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_STATE, "rxon chan %d flags %x\n",
45947370bffcSMatthew Dillon sc->rxon.chan, sc->rxon.flags);
45957370bffcSMatthew Dillon
45967370bffcSMatthew Dillon if ((error = wpi_send_rxon(sc, 0, 1)) != 0) {
45977370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: could not send RXON\n",
45987370bffcSMatthew Dillon __func__);
45997370bffcSMatthew Dillon return error;
46007370bffcSMatthew Dillon }
46017370bffcSMatthew Dillon
46027370bffcSMatthew Dillon /* Start periodic calibration timer. */
46037370bffcSMatthew Dillon callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc);
46047370bffcSMatthew Dillon
46057370bffcSMatthew Dillon WPI_RXON_UNLOCK(sc);
46067370bffcSMatthew Dillon
46077370bffcSMatthew Dillon if (vap->iv_opmode == IEEE80211_M_IBSS ||
46087370bffcSMatthew Dillon vap->iv_opmode == IEEE80211_M_HOSTAP) {
46097370bffcSMatthew Dillon if ((error = wpi_setup_beacon(sc, ni)) != 0) {
46107370bffcSMatthew Dillon device_printf(sc->sc_dev,
46117370bffcSMatthew Dillon "%s: could not setup beacon, error %d\n", __func__,
46127370bffcSMatthew Dillon error);
46137370bffcSMatthew Dillon return error;
46147370bffcSMatthew Dillon }
46157370bffcSMatthew Dillon }
46167370bffcSMatthew Dillon
46177370bffcSMatthew Dillon if (vap->iv_opmode == IEEE80211_M_STA) {
46187370bffcSMatthew Dillon /* Add BSS node. */
46197370bffcSMatthew Dillon WPI_NT_LOCK(sc);
46207370bffcSMatthew Dillon error = wpi_add_sta_node(sc, ni);
46217370bffcSMatthew Dillon WPI_NT_UNLOCK(sc);
46227370bffcSMatthew Dillon if (error != 0) {
46237370bffcSMatthew Dillon device_printf(sc->sc_dev,
46247370bffcSMatthew Dillon "%s: could not add BSS node, error %d\n", __func__,
46257370bffcSMatthew Dillon error);
46267370bffcSMatthew Dillon return error;
46277370bffcSMatthew Dillon }
46287370bffcSMatthew Dillon }
46297370bffcSMatthew Dillon
46307370bffcSMatthew Dillon /* Link LED always on while associated. */
46317370bffcSMatthew Dillon wpi_set_led(sc, WPI_LED_LINK, 0, 1);
46327370bffcSMatthew Dillon
46337370bffcSMatthew Dillon /* Enable power-saving mode if requested by user. */
46347370bffcSMatthew Dillon if ((vap->iv_flags & IEEE80211_F_PMGTON) &&
46357370bffcSMatthew Dillon vap->iv_opmode != IEEE80211_M_IBSS)
46367370bffcSMatthew Dillon (void)wpi_set_pslevel(sc, 0, 3, 1);
46377370bffcSMatthew Dillon
46387370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
46397370bffcSMatthew Dillon
46407370bffcSMatthew Dillon return 0;
46417370bffcSMatthew Dillon }
46427370bffcSMatthew Dillon
46437370bffcSMatthew Dillon static int
wpi_load_key(struct ieee80211_node * ni,const struct ieee80211_key * k)46447370bffcSMatthew Dillon wpi_load_key(struct ieee80211_node *ni, const struct ieee80211_key *k)
46457370bffcSMatthew Dillon {
46467370bffcSMatthew Dillon const struct ieee80211_cipher *cip = k->wk_cipher;
46477370bffcSMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
46487370bffcSMatthew Dillon struct wpi_softc *sc = ni->ni_ic->ic_softc;
46497370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
46507370bffcSMatthew Dillon struct wpi_node_info node;
46517370bffcSMatthew Dillon uint16_t kflags;
46527370bffcSMatthew Dillon int error;
46537370bffcSMatthew Dillon
46547370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
46557370bffcSMatthew Dillon
46567370bffcSMatthew Dillon if (wpi_check_node_entry(sc, wn->id) == 0) {
46577370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: node does not exist\n",
46587370bffcSMatthew Dillon __func__);
46597370bffcSMatthew Dillon return 0;
46607370bffcSMatthew Dillon }
46617370bffcSMatthew Dillon
46627370bffcSMatthew Dillon switch (cip->ic_cipher) {
46637370bffcSMatthew Dillon case IEEE80211_CIPHER_AES_CCM:
46647370bffcSMatthew Dillon kflags = WPI_KFLAG_CCMP;
46657370bffcSMatthew Dillon break;
46667370bffcSMatthew Dillon
46677370bffcSMatthew Dillon default:
46687370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: unknown cipher %d\n", __func__,
46697370bffcSMatthew Dillon cip->ic_cipher);
46707370bffcSMatthew Dillon return 0;
46717370bffcSMatthew Dillon }
46727370bffcSMatthew Dillon
46737370bffcSMatthew Dillon kflags |= WPI_KFLAG_KID(k->wk_keyix);
46747370bffcSMatthew Dillon if (k->wk_flags & IEEE80211_KEY_GROUP)
46757370bffcSMatthew Dillon kflags |= WPI_KFLAG_MULTICAST;
46767370bffcSMatthew Dillon
46777370bffcSMatthew Dillon memset(&node, 0, sizeof node);
46787370bffcSMatthew Dillon node.id = wn->id;
46797370bffcSMatthew Dillon node.control = WPI_NODE_UPDATE;
46807370bffcSMatthew Dillon node.flags = WPI_FLAG_KEY_SET;
46817370bffcSMatthew Dillon node.kflags = htole16(kflags);
46827370bffcSMatthew Dillon memcpy(node.key, k->wk_key, k->wk_keylen);
46837370bffcSMatthew Dillon again:
46847370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_KEY,
46857370bffcSMatthew Dillon "%s: setting %s key id %d for node %d (%s)\n", __func__,
46867370bffcSMatthew Dillon (kflags & WPI_KFLAG_MULTICAST) ? "group" : "ucast", k->wk_keyix,
46877370bffcSMatthew Dillon node.id, ether_sprintf(ni->ni_macaddr));
46887370bffcSMatthew Dillon
46897370bffcSMatthew Dillon error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1);
46907370bffcSMatthew Dillon if (error != 0) {
46917370bffcSMatthew Dillon device_printf(sc->sc_dev, "can't update node info, error %d\n",
46927370bffcSMatthew Dillon error);
46937370bffcSMatthew Dillon return !error;
46947370bffcSMatthew Dillon }
46957370bffcSMatthew Dillon
46967370bffcSMatthew Dillon if (!(kflags & WPI_KFLAG_MULTICAST) && &vap->iv_nw_keys[0] <= k &&
46977370bffcSMatthew Dillon k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) {
46987370bffcSMatthew Dillon kflags |= WPI_KFLAG_MULTICAST;
46997370bffcSMatthew Dillon node.kflags = htole16(kflags);
47007370bffcSMatthew Dillon
47017370bffcSMatthew Dillon goto again;
47027370bffcSMatthew Dillon }
47037370bffcSMatthew Dillon
47047370bffcSMatthew Dillon return 1;
47057370bffcSMatthew Dillon }
47067370bffcSMatthew Dillon
47077370bffcSMatthew Dillon static void
wpi_load_key_cb(void * arg,struct ieee80211_node * ni)47087370bffcSMatthew Dillon wpi_load_key_cb(void *arg, struct ieee80211_node *ni)
47097370bffcSMatthew Dillon {
47107370bffcSMatthew Dillon const struct ieee80211_key *k = arg;
47117370bffcSMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
47127370bffcSMatthew Dillon struct wpi_softc *sc = ni->ni_ic->ic_softc;
47137370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
47147370bffcSMatthew Dillon int error;
47157370bffcSMatthew Dillon
47167370bffcSMatthew Dillon if (vap->iv_bss == ni && wn->id == WPI_ID_UNDEFINED)
47177370bffcSMatthew Dillon return;
47187370bffcSMatthew Dillon
47197370bffcSMatthew Dillon WPI_NT_LOCK(sc);
47207370bffcSMatthew Dillon error = wpi_load_key(ni, k);
47217370bffcSMatthew Dillon WPI_NT_UNLOCK(sc);
47227370bffcSMatthew Dillon
47237370bffcSMatthew Dillon if (error == 0) {
47247370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: error while setting key\n",
47257370bffcSMatthew Dillon __func__);
47267370bffcSMatthew Dillon }
47277370bffcSMatthew Dillon }
47287370bffcSMatthew Dillon
47297370bffcSMatthew Dillon static int
wpi_set_global_keys(struct ieee80211_node * ni)47307370bffcSMatthew Dillon wpi_set_global_keys(struct ieee80211_node *ni)
47317370bffcSMatthew Dillon {
47327370bffcSMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
47337370bffcSMatthew Dillon struct ieee80211_key *wk = &vap->iv_nw_keys[0];
47347370bffcSMatthew Dillon int error = 1;
47357370bffcSMatthew Dillon
47367370bffcSMatthew Dillon for (; wk < &vap->iv_nw_keys[IEEE80211_WEP_NKID] && error; wk++)
47377370bffcSMatthew Dillon if (wk->wk_keyix != IEEE80211_KEYIX_NONE)
47387370bffcSMatthew Dillon error = wpi_load_key(ni, wk);
47397370bffcSMatthew Dillon
47407370bffcSMatthew Dillon return !error;
47417370bffcSMatthew Dillon }
47427370bffcSMatthew Dillon
47437370bffcSMatthew Dillon static int
wpi_del_key(struct ieee80211_node * ni,const struct ieee80211_key * k)47447370bffcSMatthew Dillon wpi_del_key(struct ieee80211_node *ni, const struct ieee80211_key *k)
47457370bffcSMatthew Dillon {
47467370bffcSMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
47477370bffcSMatthew Dillon struct wpi_softc *sc = ni->ni_ic->ic_softc;
47487370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
47497370bffcSMatthew Dillon struct wpi_node_info node;
47507370bffcSMatthew Dillon uint16_t kflags;
47517370bffcSMatthew Dillon int error;
47527370bffcSMatthew Dillon
47537370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
47547370bffcSMatthew Dillon
47557370bffcSMatthew Dillon if (wpi_check_node_entry(sc, wn->id) == 0) {
47567370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_KEY, "%s: node was removed\n", __func__);
47577370bffcSMatthew Dillon return 1; /* Nothing to do. */
47587370bffcSMatthew Dillon }
47597370bffcSMatthew Dillon
47607370bffcSMatthew Dillon kflags = WPI_KFLAG_KID(k->wk_keyix);
47617370bffcSMatthew Dillon if (k->wk_flags & IEEE80211_KEY_GROUP)
47627370bffcSMatthew Dillon kflags |= WPI_KFLAG_MULTICAST;
47637370bffcSMatthew Dillon
47647370bffcSMatthew Dillon memset(&node, 0, sizeof node);
47657370bffcSMatthew Dillon node.id = wn->id;
47667370bffcSMatthew Dillon node.control = WPI_NODE_UPDATE;
47677370bffcSMatthew Dillon node.flags = WPI_FLAG_KEY_SET;
47687370bffcSMatthew Dillon node.kflags = htole16(kflags);
47697370bffcSMatthew Dillon again:
47707370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_KEY, "%s: deleting %s key %d for node %d (%s)\n",
47717370bffcSMatthew Dillon __func__, (kflags & WPI_KFLAG_MULTICAST) ? "group" : "ucast",
47727370bffcSMatthew Dillon k->wk_keyix, node.id, ether_sprintf(ni->ni_macaddr));
47737370bffcSMatthew Dillon
47747370bffcSMatthew Dillon error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1);
47757370bffcSMatthew Dillon if (error != 0) {
47767370bffcSMatthew Dillon device_printf(sc->sc_dev, "can't update node info, error %d\n",
47777370bffcSMatthew Dillon error);
47787370bffcSMatthew Dillon return !error;
47797370bffcSMatthew Dillon }
47807370bffcSMatthew Dillon
47817370bffcSMatthew Dillon if (!(kflags & WPI_KFLAG_MULTICAST) && &vap->iv_nw_keys[0] <= k &&
47827370bffcSMatthew Dillon k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) {
47837370bffcSMatthew Dillon kflags |= WPI_KFLAG_MULTICAST;
47847370bffcSMatthew Dillon node.kflags = htole16(kflags);
47857370bffcSMatthew Dillon
47867370bffcSMatthew Dillon goto again;
47877370bffcSMatthew Dillon }
47887370bffcSMatthew Dillon
47897370bffcSMatthew Dillon return 1;
47907370bffcSMatthew Dillon }
47917370bffcSMatthew Dillon
47927370bffcSMatthew Dillon static void
wpi_del_key_cb(void * arg,struct ieee80211_node * ni)47937370bffcSMatthew Dillon wpi_del_key_cb(void *arg, struct ieee80211_node *ni)
47947370bffcSMatthew Dillon {
47957370bffcSMatthew Dillon const struct ieee80211_key *k = arg;
47967370bffcSMatthew Dillon struct ieee80211vap *vap = ni->ni_vap;
47977370bffcSMatthew Dillon struct wpi_softc *sc = ni->ni_ic->ic_softc;
47987370bffcSMatthew Dillon struct wpi_node *wn = WPI_NODE(ni);
47997370bffcSMatthew Dillon int error;
48007370bffcSMatthew Dillon
48017370bffcSMatthew Dillon if (vap->iv_bss == ni && wn->id == WPI_ID_UNDEFINED)
48027370bffcSMatthew Dillon return;
48037370bffcSMatthew Dillon
48047370bffcSMatthew Dillon WPI_NT_LOCK(sc);
48057370bffcSMatthew Dillon error = wpi_del_key(ni, k);
48067370bffcSMatthew Dillon WPI_NT_UNLOCK(sc);
48077370bffcSMatthew Dillon
48087370bffcSMatthew Dillon if (error == 0) {
48097370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: error while deleting key\n",
48107370bffcSMatthew Dillon __func__);
48117370bffcSMatthew Dillon }
48127370bffcSMatthew Dillon }
48137370bffcSMatthew Dillon
48147370bffcSMatthew Dillon static int
wpi_process_key(struct ieee80211vap * vap,const struct ieee80211_key * k,int set)48157370bffcSMatthew Dillon wpi_process_key(struct ieee80211vap *vap, const struct ieee80211_key *k,
48167370bffcSMatthew Dillon int set)
48177370bffcSMatthew Dillon {
48187370bffcSMatthew Dillon struct ieee80211com *ic = vap->iv_ic;
48197370bffcSMatthew Dillon struct wpi_softc *sc = ic->ic_softc;
48207370bffcSMatthew Dillon struct wpi_vap *wvp = WPI_VAP(vap);
48217370bffcSMatthew Dillon struct ieee80211_node *ni;
48227370bffcSMatthew Dillon int error, ni_ref = 0;
48237370bffcSMatthew Dillon
48247370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
48257370bffcSMatthew Dillon
48267370bffcSMatthew Dillon if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
48277370bffcSMatthew Dillon /* Not for us. */
48287370bffcSMatthew Dillon return 1;
48297370bffcSMatthew Dillon }
48307370bffcSMatthew Dillon
48317370bffcSMatthew Dillon if (!(k->wk_flags & IEEE80211_KEY_RECV)) {
48327370bffcSMatthew Dillon /* XMIT keys are handled in wpi_tx_data(). */
48337370bffcSMatthew Dillon return 1;
48347370bffcSMatthew Dillon }
48357370bffcSMatthew Dillon
48367370bffcSMatthew Dillon /* Handle group keys. */
48377370bffcSMatthew Dillon if (&vap->iv_nw_keys[0] <= k &&
48387370bffcSMatthew Dillon k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) {
48397370bffcSMatthew Dillon WPI_NT_LOCK(sc);
48407370bffcSMatthew Dillon if (set)
48417370bffcSMatthew Dillon wvp->wv_gtk |= WPI_VAP_KEY(k->wk_keyix);
48427370bffcSMatthew Dillon else
48437370bffcSMatthew Dillon wvp->wv_gtk &= ~WPI_VAP_KEY(k->wk_keyix);
48447370bffcSMatthew Dillon WPI_NT_UNLOCK(sc);
48457370bffcSMatthew Dillon
48467370bffcSMatthew Dillon if (vap->iv_state == IEEE80211_S_RUN) {
48477370bffcSMatthew Dillon ieee80211_iterate_nodes(&ic->ic_sta,
48487370bffcSMatthew Dillon set ? wpi_load_key_cb : wpi_del_key_cb,
48497370bffcSMatthew Dillon __DECONST(void *, k));
48507370bffcSMatthew Dillon }
48517370bffcSMatthew Dillon
48527370bffcSMatthew Dillon return 1;
48537370bffcSMatthew Dillon }
48547370bffcSMatthew Dillon
48557370bffcSMatthew Dillon switch (vap->iv_opmode) {
48567370bffcSMatthew Dillon case IEEE80211_M_STA:
48577370bffcSMatthew Dillon ni = vap->iv_bss;
48587370bffcSMatthew Dillon break;
48597370bffcSMatthew Dillon
48607370bffcSMatthew Dillon case IEEE80211_M_IBSS:
48617370bffcSMatthew Dillon case IEEE80211_M_AHDEMO:
48627370bffcSMatthew Dillon case IEEE80211_M_HOSTAP:
48637370bffcSMatthew Dillon ni = ieee80211_find_vap_node(&ic->ic_sta, vap, k->wk_macaddr);
48647370bffcSMatthew Dillon if (ni == NULL)
48657370bffcSMatthew Dillon return 0; /* should not happen */
48667370bffcSMatthew Dillon
48677370bffcSMatthew Dillon ni_ref = 1;
48687370bffcSMatthew Dillon break;
48697370bffcSMatthew Dillon
48707370bffcSMatthew Dillon default:
48717370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: unknown opmode %d\n", __func__,
48727370bffcSMatthew Dillon vap->iv_opmode);
48737370bffcSMatthew Dillon return 0;
48747370bffcSMatthew Dillon }
48757370bffcSMatthew Dillon
48767370bffcSMatthew Dillon WPI_NT_LOCK(sc);
48777370bffcSMatthew Dillon if (set)
48787370bffcSMatthew Dillon error = wpi_load_key(ni, k);
48797370bffcSMatthew Dillon else
48807370bffcSMatthew Dillon error = wpi_del_key(ni, k);
48817370bffcSMatthew Dillon WPI_NT_UNLOCK(sc);
48827370bffcSMatthew Dillon
48837370bffcSMatthew Dillon if (ni_ref)
48847370bffcSMatthew Dillon ieee80211_node_decref(ni);
48857370bffcSMatthew Dillon
48867370bffcSMatthew Dillon return error;
48877370bffcSMatthew Dillon }
48887370bffcSMatthew Dillon
48897370bffcSMatthew Dillon static int
wpi_key_set(struct ieee80211vap * vap,const struct ieee80211_key * k)48907370bffcSMatthew Dillon wpi_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
48917370bffcSMatthew Dillon {
48927370bffcSMatthew Dillon return wpi_process_key(vap, k, 1);
48937370bffcSMatthew Dillon }
48947370bffcSMatthew Dillon
48957370bffcSMatthew Dillon static int
wpi_key_delete(struct ieee80211vap * vap,const struct ieee80211_key * k)48967370bffcSMatthew Dillon wpi_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
48977370bffcSMatthew Dillon {
48987370bffcSMatthew Dillon return wpi_process_key(vap, k, 0);
48997370bffcSMatthew Dillon }
49007370bffcSMatthew Dillon
49017370bffcSMatthew Dillon /*
49027370bffcSMatthew Dillon * This function is called after the runtime firmware notifies us of its
49037370bffcSMatthew Dillon * readiness (called in a process context).
49047370bffcSMatthew Dillon */
49057370bffcSMatthew Dillon static int
wpi_post_alive(struct wpi_softc * sc)49067370bffcSMatthew Dillon wpi_post_alive(struct wpi_softc *sc)
49077370bffcSMatthew Dillon {
49087370bffcSMatthew Dillon int ntries, error;
49097370bffcSMatthew Dillon
49107370bffcSMatthew Dillon /* Check (again) that the radio is not disabled. */
49117370bffcSMatthew Dillon if ((error = wpi_nic_lock(sc)) != 0)
49127370bffcSMatthew Dillon return error;
49137370bffcSMatthew Dillon
49147370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
49157370bffcSMatthew Dillon
49167370bffcSMatthew Dillon /* NB: Runtime firmware must be up and running. */
49177370bffcSMatthew Dillon if (!(wpi_prph_read(sc, WPI_APMG_RFKILL) & 1)) {
49187370bffcSMatthew Dillon device_printf(sc->sc_dev,
49197370bffcSMatthew Dillon "RF switch: radio disabled (%s)\n", __func__);
49207370bffcSMatthew Dillon wpi_nic_unlock(sc);
49217370bffcSMatthew Dillon return EPERM; /* :-) */
49227370bffcSMatthew Dillon }
49237370bffcSMatthew Dillon wpi_nic_unlock(sc);
49247370bffcSMatthew Dillon
49257370bffcSMatthew Dillon /* Wait for thermal sensor to calibrate. */
49267370bffcSMatthew Dillon for (ntries = 0; ntries < 1000; ntries++) {
49277370bffcSMatthew Dillon if ((sc->temp = (int)WPI_READ(sc, WPI_UCODE_GP2)) != 0)
49287370bffcSMatthew Dillon break;
49297370bffcSMatthew Dillon DELAY(10);
49307370bffcSMatthew Dillon }
49317370bffcSMatthew Dillon
49327370bffcSMatthew Dillon if (ntries == 1000) {
49337370bffcSMatthew Dillon device_printf(sc->sc_dev,
49347370bffcSMatthew Dillon "timeout waiting for thermal sensor calibration\n");
49357370bffcSMatthew Dillon return ETIMEDOUT;
49367370bffcSMatthew Dillon }
49377370bffcSMatthew Dillon
49387370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TEMP, "temperature %d\n", sc->temp);
49397370bffcSMatthew Dillon return 0;
49407370bffcSMatthew Dillon }
49417370bffcSMatthew Dillon
49427370bffcSMatthew Dillon /*
49437370bffcSMatthew Dillon * The firmware boot code is small and is intended to be copied directly into
49447370bffcSMatthew Dillon * the NIC internal memory (no DMA transfer).
49457370bffcSMatthew Dillon */
49467370bffcSMatthew Dillon static int
wpi_load_bootcode(struct wpi_softc * sc,const uint8_t * ucode,uint32_t size)49477370bffcSMatthew Dillon wpi_load_bootcode(struct wpi_softc *sc, const uint8_t *ucode, uint32_t size)
49487370bffcSMatthew Dillon {
49497370bffcSMatthew Dillon int error, ntries;
49507370bffcSMatthew Dillon
49517370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_HW, "Loading microcode size 0x%x\n", size);
49527370bffcSMatthew Dillon
49537370bffcSMatthew Dillon size /= sizeof (uint32_t);
49547370bffcSMatthew Dillon
49557370bffcSMatthew Dillon if ((error = wpi_nic_lock(sc)) != 0)
49567370bffcSMatthew Dillon return error;
49577370bffcSMatthew Dillon
49587370bffcSMatthew Dillon /* Copy microcode image into NIC memory. */
49597370bffcSMatthew Dillon wpi_prph_write_region_4(sc, WPI_BSM_SRAM_BASE,
49607370bffcSMatthew Dillon (const uint32_t *)ucode, size);
49617370bffcSMatthew Dillon
49627370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_WR_MEM_SRC, 0);
49637370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_WR_MEM_DST, WPI_FW_TEXT_BASE);
49647370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_WR_DWCOUNT, size);
49657370bffcSMatthew Dillon
49667370bffcSMatthew Dillon /* Start boot load now. */
49677370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_WR_CTRL, WPI_BSM_WR_CTRL_START);
49687370bffcSMatthew Dillon
49697370bffcSMatthew Dillon /* Wait for transfer to complete. */
49707370bffcSMatthew Dillon for (ntries = 0; ntries < 1000; ntries++) {
49717370bffcSMatthew Dillon uint32_t status = WPI_READ(sc, WPI_FH_TX_STATUS);
49727370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_HW,
49737370bffcSMatthew Dillon "firmware status=0x%x, val=0x%x, result=0x%x\n", status,
49747370bffcSMatthew Dillon WPI_FH_TX_STATUS_IDLE(6),
49757370bffcSMatthew Dillon status & WPI_FH_TX_STATUS_IDLE(6));
49767370bffcSMatthew Dillon if (status & WPI_FH_TX_STATUS_IDLE(6)) {
49777370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_HW,
49787370bffcSMatthew Dillon "Status Match! - ntries = %d\n", ntries);
49797370bffcSMatthew Dillon break;
49807370bffcSMatthew Dillon }
49817370bffcSMatthew Dillon DELAY(10);
49827370bffcSMatthew Dillon }
49837370bffcSMatthew Dillon if (ntries == 1000) {
49847370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
49857370bffcSMatthew Dillon __func__);
49867370bffcSMatthew Dillon wpi_nic_unlock(sc);
49877370bffcSMatthew Dillon return ETIMEDOUT;
49887370bffcSMatthew Dillon }
49897370bffcSMatthew Dillon
49907370bffcSMatthew Dillon /* Enable boot after power up. */
49917370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_WR_CTRL, WPI_BSM_WR_CTRL_START_EN);
49927370bffcSMatthew Dillon
49937370bffcSMatthew Dillon wpi_nic_unlock(sc);
49947370bffcSMatthew Dillon return 0;
49957370bffcSMatthew Dillon }
49967370bffcSMatthew Dillon
49977370bffcSMatthew Dillon static int
wpi_load_firmware(struct wpi_softc * sc)49987370bffcSMatthew Dillon wpi_load_firmware(struct wpi_softc *sc)
49997370bffcSMatthew Dillon {
50007370bffcSMatthew Dillon struct wpi_fw_info *fw = &sc->fw;
50017370bffcSMatthew Dillon struct wpi_dma_info *dma = &sc->fw_dma;
50027370bffcSMatthew Dillon int error;
50037370bffcSMatthew Dillon
50047370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
50057370bffcSMatthew Dillon
50067370bffcSMatthew Dillon /* Copy initialization sections into pre-allocated DMA-safe memory. */
50077370bffcSMatthew Dillon memcpy(dma->vaddr, fw->init.data, fw->init.datasz);
50087370bffcSMatthew Dillon bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
50097370bffcSMatthew Dillon memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, fw->init.text, fw->init.textsz);
50107370bffcSMatthew Dillon bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
50117370bffcSMatthew Dillon
50127370bffcSMatthew Dillon /* Tell adapter where to find initialization sections. */
50137370bffcSMatthew Dillon if ((error = wpi_nic_lock(sc)) != 0)
50147370bffcSMatthew Dillon return error;
50157370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_DRAM_DATA_ADDR, dma->paddr);
50167370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_DRAM_DATA_SIZE, fw->init.datasz);
50177370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_ADDR,
50187370bffcSMatthew Dillon dma->paddr + WPI_FW_DATA_MAXSZ);
50197370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_SIZE, fw->init.textsz);
50207370bffcSMatthew Dillon wpi_nic_unlock(sc);
50217370bffcSMatthew Dillon
50227370bffcSMatthew Dillon /* Load firmware boot code. */
50237370bffcSMatthew Dillon error = wpi_load_bootcode(sc, fw->boot.text, fw->boot.textsz);
50247370bffcSMatthew Dillon if (error != 0) {
50257370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: could not load boot firmware\n",
50267370bffcSMatthew Dillon __func__);
50277370bffcSMatthew Dillon return error;
50287370bffcSMatthew Dillon }
50297370bffcSMatthew Dillon
50307370bffcSMatthew Dillon /* Now press "execute". */
50317370bffcSMatthew Dillon WPI_WRITE(sc, WPI_RESET, 0);
50327370bffcSMatthew Dillon
50337370bffcSMatthew Dillon /* Wait at most one second for first alive notification. */
50347370bffcSMatthew Dillon #if defined(__DragonFly__)
50357370bffcSMatthew Dillon if ((error = lksleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) {
50367370bffcSMatthew Dillon #else
50377370bffcSMatthew Dillon if ((error = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) {
50387370bffcSMatthew Dillon #endif
50397370bffcSMatthew Dillon device_printf(sc->sc_dev,
50407370bffcSMatthew Dillon "%s: timeout waiting for adapter to initialize, error %d\n",
50417370bffcSMatthew Dillon __func__, error);
50427370bffcSMatthew Dillon return error;
50437370bffcSMatthew Dillon }
50447370bffcSMatthew Dillon
50457370bffcSMatthew Dillon /* Copy runtime sections into pre-allocated DMA-safe memory. */
50467370bffcSMatthew Dillon memcpy(dma->vaddr, fw->main.data, fw->main.datasz);
50477370bffcSMatthew Dillon bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
50487370bffcSMatthew Dillon memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, fw->main.text, fw->main.textsz);
50497370bffcSMatthew Dillon bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
50507370bffcSMatthew Dillon
50517370bffcSMatthew Dillon /* Tell adapter where to find runtime sections. */
50527370bffcSMatthew Dillon if ((error = wpi_nic_lock(sc)) != 0)
50537370bffcSMatthew Dillon return error;
50547370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_DRAM_DATA_ADDR, dma->paddr);
50557370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_DRAM_DATA_SIZE, fw->main.datasz);
50567370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_ADDR,
50577370bffcSMatthew Dillon dma->paddr + WPI_FW_DATA_MAXSZ);
50587370bffcSMatthew Dillon wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_SIZE,
50597370bffcSMatthew Dillon WPI_FW_UPDATED | fw->main.textsz);
50607370bffcSMatthew Dillon wpi_nic_unlock(sc);
50617370bffcSMatthew Dillon
50627370bffcSMatthew Dillon return 0;
50637370bffcSMatthew Dillon }
50647370bffcSMatthew Dillon
50657370bffcSMatthew Dillon static int
50667370bffcSMatthew Dillon wpi_read_firmware(struct wpi_softc *sc)
50677370bffcSMatthew Dillon {
50687370bffcSMatthew Dillon const struct firmware *fp;
50697370bffcSMatthew Dillon struct wpi_fw_info *fw = &sc->fw;
50707370bffcSMatthew Dillon const struct wpi_firmware_hdr *hdr;
50717370bffcSMatthew Dillon int error;
50727370bffcSMatthew Dillon
50737370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
50747370bffcSMatthew Dillon
50757370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_FIRMWARE,
50767370bffcSMatthew Dillon "Attempting Loading Firmware from %s module\n", WPI_FW_NAME);
50777370bffcSMatthew Dillon
50787370bffcSMatthew Dillon WPI_UNLOCK(sc);
50797370bffcSMatthew Dillon fp = firmware_get(WPI_FW_NAME);
50807370bffcSMatthew Dillon WPI_LOCK(sc);
50817370bffcSMatthew Dillon
50827370bffcSMatthew Dillon if (fp == NULL) {
50837370bffcSMatthew Dillon device_printf(sc->sc_dev,
50847370bffcSMatthew Dillon "could not load firmware image '%s'\n", WPI_FW_NAME);
50857370bffcSMatthew Dillon return EINVAL;
50867370bffcSMatthew Dillon }
50877370bffcSMatthew Dillon
50887370bffcSMatthew Dillon sc->fw_fp = fp;
50897370bffcSMatthew Dillon
50907370bffcSMatthew Dillon if (fp->datasize < sizeof (struct wpi_firmware_hdr)) {
50917370bffcSMatthew Dillon device_printf(sc->sc_dev,
50927370bffcSMatthew Dillon "firmware file too short: %zu bytes\n", fp->datasize);
50937370bffcSMatthew Dillon error = EINVAL;
50947370bffcSMatthew Dillon goto fail;
50957370bffcSMatthew Dillon }
50967370bffcSMatthew Dillon
50977370bffcSMatthew Dillon fw->size = fp->datasize;
50987370bffcSMatthew Dillon fw->data = (const uint8_t *)fp->data;
50997370bffcSMatthew Dillon
51007370bffcSMatthew Dillon /* Extract firmware header information. */
51017370bffcSMatthew Dillon hdr = (const struct wpi_firmware_hdr *)fw->data;
51027370bffcSMatthew Dillon
51037370bffcSMatthew Dillon /* | RUNTIME FIRMWARE | INIT FIRMWARE | BOOT FW |
51047370bffcSMatthew Dillon |HDR|<--TEXT-->|<--DATA-->|<--TEXT-->|<--DATA-->|<--TEXT-->| */
51057370bffcSMatthew Dillon
51067370bffcSMatthew Dillon fw->main.textsz = le32toh(hdr->rtextsz);
51077370bffcSMatthew Dillon fw->main.datasz = le32toh(hdr->rdatasz);
51087370bffcSMatthew Dillon fw->init.textsz = le32toh(hdr->itextsz);
51097370bffcSMatthew Dillon fw->init.datasz = le32toh(hdr->idatasz);
51107370bffcSMatthew Dillon fw->boot.textsz = le32toh(hdr->btextsz);
51117370bffcSMatthew Dillon fw->boot.datasz = 0;
51127370bffcSMatthew Dillon
51137370bffcSMatthew Dillon /* Sanity-check firmware header. */
51147370bffcSMatthew Dillon if (fw->main.textsz > WPI_FW_TEXT_MAXSZ ||
51157370bffcSMatthew Dillon fw->main.datasz > WPI_FW_DATA_MAXSZ ||
51167370bffcSMatthew Dillon fw->init.textsz > WPI_FW_TEXT_MAXSZ ||
51177370bffcSMatthew Dillon fw->init.datasz > WPI_FW_DATA_MAXSZ ||
51187370bffcSMatthew Dillon fw->boot.textsz > WPI_FW_BOOT_TEXT_MAXSZ ||
51197370bffcSMatthew Dillon (fw->boot.textsz & 3) != 0) {
51207370bffcSMatthew Dillon device_printf(sc->sc_dev, "invalid firmware header\n");
51217370bffcSMatthew Dillon error = EINVAL;
51227370bffcSMatthew Dillon goto fail;
51237370bffcSMatthew Dillon }
51247370bffcSMatthew Dillon
51257370bffcSMatthew Dillon /* Check that all firmware sections fit. */
51267370bffcSMatthew Dillon if (fw->size < sizeof (*hdr) + fw->main.textsz + fw->main.datasz +
51277370bffcSMatthew Dillon fw->init.textsz + fw->init.datasz + fw->boot.textsz) {
51287370bffcSMatthew Dillon device_printf(sc->sc_dev,
51297370bffcSMatthew Dillon "firmware file too short: %zu bytes\n", fw->size);
51307370bffcSMatthew Dillon error = EINVAL;
51317370bffcSMatthew Dillon goto fail;
51327370bffcSMatthew Dillon }
51337370bffcSMatthew Dillon
51347370bffcSMatthew Dillon /* Get pointers to firmware sections. */
51357370bffcSMatthew Dillon fw->main.text = (const uint8_t *)(hdr + 1);
51367370bffcSMatthew Dillon fw->main.data = fw->main.text + fw->main.textsz;
51377370bffcSMatthew Dillon fw->init.text = fw->main.data + fw->main.datasz;
51387370bffcSMatthew Dillon fw->init.data = fw->init.text + fw->init.textsz;
51397370bffcSMatthew Dillon fw->boot.text = fw->init.data + fw->init.datasz;
51407370bffcSMatthew Dillon
51417370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_FIRMWARE,
51427370bffcSMatthew Dillon "Firmware Version: Major %d, Minor %d, Driver %d, \n"
51437370bffcSMatthew Dillon "runtime (text: %u, data: %u) init (text: %u, data %u) "
51447370bffcSMatthew Dillon "boot (text %u)\n", hdr->major, hdr->minor, le32toh(hdr->driver),
51457370bffcSMatthew Dillon fw->main.textsz, fw->main.datasz,
51467370bffcSMatthew Dillon fw->init.textsz, fw->init.datasz, fw->boot.textsz);
51477370bffcSMatthew Dillon
51487370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->main.text %p\n", fw->main.text);
51497370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->main.data %p\n", fw->main.data);
51507370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->init.text %p\n", fw->init.text);
51517370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->init.data %p\n", fw->init.data);
51527370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->boot.text %p\n", fw->boot.text);
51537370bffcSMatthew Dillon
51547370bffcSMatthew Dillon return 0;
51557370bffcSMatthew Dillon
51567370bffcSMatthew Dillon fail: wpi_unload_firmware(sc);
51577370bffcSMatthew Dillon return error;
51587370bffcSMatthew Dillon }
51597370bffcSMatthew Dillon
51607370bffcSMatthew Dillon /**
51617370bffcSMatthew Dillon * Free the referenced firmware image
51627370bffcSMatthew Dillon */
51637370bffcSMatthew Dillon static void
51647370bffcSMatthew Dillon wpi_unload_firmware(struct wpi_softc *sc)
51657370bffcSMatthew Dillon {
51667370bffcSMatthew Dillon if (sc->fw_fp != NULL) {
51677370bffcSMatthew Dillon firmware_put(sc->fw_fp, FIRMWARE_UNLOAD);
51687370bffcSMatthew Dillon sc->fw_fp = NULL;
51697370bffcSMatthew Dillon }
51707370bffcSMatthew Dillon }
51717370bffcSMatthew Dillon
51727370bffcSMatthew Dillon static int
51737370bffcSMatthew Dillon wpi_clock_wait(struct wpi_softc *sc)
51747370bffcSMatthew Dillon {
51757370bffcSMatthew Dillon int ntries;
51767370bffcSMatthew Dillon
51777370bffcSMatthew Dillon /* Set "initialization complete" bit. */
51787370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_INIT_DONE);
51797370bffcSMatthew Dillon
51807370bffcSMatthew Dillon /* Wait for clock stabilization. */
51817370bffcSMatthew Dillon for (ntries = 0; ntries < 2500; ntries++) {
51827370bffcSMatthew Dillon if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_MAC_CLOCK_READY)
51837370bffcSMatthew Dillon return 0;
51847370bffcSMatthew Dillon DELAY(100);
51857370bffcSMatthew Dillon }
51867370bffcSMatthew Dillon device_printf(sc->sc_dev,
51877370bffcSMatthew Dillon "%s: timeout waiting for clock stabilization\n", __func__);
51887370bffcSMatthew Dillon
51897370bffcSMatthew Dillon return ETIMEDOUT;
51907370bffcSMatthew Dillon }
51917370bffcSMatthew Dillon
51927370bffcSMatthew Dillon static int
51937370bffcSMatthew Dillon wpi_apm_init(struct wpi_softc *sc)
51947370bffcSMatthew Dillon {
51957370bffcSMatthew Dillon uint32_t reg;
51967370bffcSMatthew Dillon int error;
51977370bffcSMatthew Dillon
51987370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
51997370bffcSMatthew Dillon
52007370bffcSMatthew Dillon /* Disable L0s exit timer (NMI bug workaround). */
52017370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_GIO_CHICKEN, WPI_GIO_CHICKEN_DIS_L0S_TIMER);
52027370bffcSMatthew Dillon /* Don't wait for ICH L0s (ICH bug workaround). */
52037370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_GIO_CHICKEN, WPI_GIO_CHICKEN_L1A_NO_L0S_RX);
52047370bffcSMatthew Dillon
52057370bffcSMatthew Dillon /* Set FH wait threshold to max (HW bug under stress workaround). */
52067370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_DBG_HPET_MEM, 0xffff0000);
52077370bffcSMatthew Dillon
52087370bffcSMatthew Dillon /* Retrieve PCIe Active State Power Management (ASPM). */
52097370bffcSMatthew Dillon #if defined(__DragonFly__)
52107370bffcSMatthew Dillon reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINKCTRL, 1);
52117370bffcSMatthew Dillon /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
52127370bffcSMatthew Dillon if (reg & PCIEM_LNKCTL_ASPM_L1) /* L1 Entry enabled. */
52137370bffcSMatthew Dillon #else
52147370bffcSMatthew Dillon reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, 1);
52157370bffcSMatthew Dillon /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
52167370bffcSMatthew Dillon if (reg & PCIEM_LINK_CTL_ASPMC_L1) /* L1 Entry enabled. */
52177370bffcSMatthew Dillon #endif
52187370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA);
52197370bffcSMatthew Dillon else
52207370bffcSMatthew Dillon WPI_CLRBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA);
52217370bffcSMatthew Dillon
52227370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_ANA_PLL, WPI_ANA_PLL_INIT);
52237370bffcSMatthew Dillon
52247370bffcSMatthew Dillon /* Wait for clock stabilization before accessing prph. */
52257370bffcSMatthew Dillon if ((error = wpi_clock_wait(sc)) != 0)
52267370bffcSMatthew Dillon return error;
52277370bffcSMatthew Dillon
52287370bffcSMatthew Dillon if ((error = wpi_nic_lock(sc)) != 0)
52297370bffcSMatthew Dillon return error;
52307370bffcSMatthew Dillon /* Cleanup. */
52317370bffcSMatthew Dillon wpi_prph_write(sc, WPI_APMG_CLK_DIS, 0x00000400);
52327370bffcSMatthew Dillon wpi_prph_clrbits(sc, WPI_APMG_PS, 0x00000200);
52337370bffcSMatthew Dillon
52347370bffcSMatthew Dillon /* Enable DMA and BSM (Bootstrap State Machine). */
52357370bffcSMatthew Dillon wpi_prph_write(sc, WPI_APMG_CLK_EN,
52367370bffcSMatthew Dillon WPI_APMG_CLK_CTRL_DMA_CLK_RQT | WPI_APMG_CLK_CTRL_BSM_CLK_RQT);
52377370bffcSMatthew Dillon DELAY(20);
52387370bffcSMatthew Dillon /* Disable L1-Active. */
52397370bffcSMatthew Dillon wpi_prph_setbits(sc, WPI_APMG_PCI_STT, WPI_APMG_PCI_STT_L1A_DIS);
52407370bffcSMatthew Dillon wpi_nic_unlock(sc);
52417370bffcSMatthew Dillon
52427370bffcSMatthew Dillon return 0;
52437370bffcSMatthew Dillon }
52447370bffcSMatthew Dillon
52457370bffcSMatthew Dillon static void
52467370bffcSMatthew Dillon wpi_apm_stop_master(struct wpi_softc *sc)
52477370bffcSMatthew Dillon {
52487370bffcSMatthew Dillon int ntries;
52497370bffcSMatthew Dillon
52507370bffcSMatthew Dillon /* Stop busmaster DMA activity. */
52517370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_RESET, WPI_RESET_STOP_MASTER);
52527370bffcSMatthew Dillon
52537370bffcSMatthew Dillon if ((WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_PS_MASK) ==
52547370bffcSMatthew Dillon WPI_GP_CNTRL_MAC_PS)
52557370bffcSMatthew Dillon return; /* Already asleep. */
52567370bffcSMatthew Dillon
52577370bffcSMatthew Dillon for (ntries = 0; ntries < 100; ntries++) {
52587370bffcSMatthew Dillon if (WPI_READ(sc, WPI_RESET) & WPI_RESET_MASTER_DISABLED)
52597370bffcSMatthew Dillon return;
52607370bffcSMatthew Dillon DELAY(10);
52617370bffcSMatthew Dillon }
52627370bffcSMatthew Dillon device_printf(sc->sc_dev, "%s: timeout waiting for master\n",
52637370bffcSMatthew Dillon __func__);
52647370bffcSMatthew Dillon }
52657370bffcSMatthew Dillon
52667370bffcSMatthew Dillon static void
52677370bffcSMatthew Dillon wpi_apm_stop(struct wpi_softc *sc)
52687370bffcSMatthew Dillon {
52697370bffcSMatthew Dillon wpi_apm_stop_master(sc);
52707370bffcSMatthew Dillon
52717370bffcSMatthew Dillon /* Reset the entire device. */
52727370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_RESET, WPI_RESET_SW);
52737370bffcSMatthew Dillon DELAY(10);
52747370bffcSMatthew Dillon /* Clear "initialization complete" bit. */
52757370bffcSMatthew Dillon WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_INIT_DONE);
52767370bffcSMatthew Dillon }
52777370bffcSMatthew Dillon
52787370bffcSMatthew Dillon static void
52797370bffcSMatthew Dillon wpi_nic_config(struct wpi_softc *sc)
52807370bffcSMatthew Dillon {
52817370bffcSMatthew Dillon uint32_t rev;
52827370bffcSMatthew Dillon
52837370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
52847370bffcSMatthew Dillon
52857370bffcSMatthew Dillon /* voodoo from the Linux "driver".. */
52867370bffcSMatthew Dillon rev = pci_read_config(sc->sc_dev, PCIR_REVID, 1);
52877370bffcSMatthew Dillon if ((rev & 0xc0) == 0x40)
52887370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MB);
52897370bffcSMatthew Dillon else if (!(rev & 0x80))
52907370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MM);
52917370bffcSMatthew Dillon
52927370bffcSMatthew Dillon if (sc->cap == 0x80)
52937370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_SKU_MRC);
52947370bffcSMatthew Dillon
52957370bffcSMatthew Dillon if ((sc->rev & 0xf0) == 0xd0)
52967370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_REV_D);
52977370bffcSMatthew Dillon else
52987370bffcSMatthew Dillon WPI_CLRBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_REV_D);
52997370bffcSMatthew Dillon
53007370bffcSMatthew Dillon if (sc->type > 1)
53017370bffcSMatthew Dillon WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_TYPE_B);
53027370bffcSMatthew Dillon }
53037370bffcSMatthew Dillon
53047370bffcSMatthew Dillon static int
53057370bffcSMatthew Dillon wpi_hw_init(struct wpi_softc *sc)
53067370bffcSMatthew Dillon {
53077370bffcSMatthew Dillon uint8_t chnl;
53087370bffcSMatthew Dillon int ntries, error;
53097370bffcSMatthew Dillon
53107370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
53117370bffcSMatthew Dillon
53127370bffcSMatthew Dillon /* Clear pending interrupts. */
53137370bffcSMatthew Dillon WPI_WRITE(sc, WPI_INT, 0xffffffff);
53147370bffcSMatthew Dillon
53157370bffcSMatthew Dillon if ((error = wpi_apm_init(sc)) != 0) {
53167370bffcSMatthew Dillon device_printf(sc->sc_dev,
53177370bffcSMatthew Dillon "%s: could not power ON adapter, error %d\n", __func__,
53187370bffcSMatthew Dillon error);
53197370bffcSMatthew Dillon return error;
53207370bffcSMatthew Dillon }
53217370bffcSMatthew Dillon
53227370bffcSMatthew Dillon /* Select VMAIN power source. */
53237370bffcSMatthew Dillon if ((error = wpi_nic_lock(sc)) != 0)
53247370bffcSMatthew Dillon return error;
53257370bffcSMatthew Dillon wpi_prph_clrbits(sc, WPI_APMG_PS, WPI_APMG_PS_PWR_SRC_MASK);
53267370bffcSMatthew Dillon wpi_nic_unlock(sc);
53277370bffcSMatthew Dillon /* Spin until VMAIN gets selected. */
53287370bffcSMatthew Dillon for (ntries = 0; ntries < 5000; ntries++) {
53297370bffcSMatthew Dillon if (WPI_READ(sc, WPI_GPIO_IN) & WPI_GPIO_IN_VMAIN)
53307370bffcSMatthew Dillon break;
53317370bffcSMatthew Dillon DELAY(10);
53327370bffcSMatthew Dillon }
53337370bffcSMatthew Dillon if (ntries == 5000) {
53347370bffcSMatthew Dillon device_printf(sc->sc_dev, "timeout selecting power source\n");
53357370bffcSMatthew Dillon return ETIMEDOUT;
53367370bffcSMatthew Dillon }
53377370bffcSMatthew Dillon
53387370bffcSMatthew Dillon /* Perform adapter initialization. */
53397370bffcSMatthew Dillon wpi_nic_config(sc);
53407370bffcSMatthew Dillon
53417370bffcSMatthew Dillon /* Initialize RX ring. */
53427370bffcSMatthew Dillon if ((error = wpi_nic_lock(sc)) != 0)
53437370bffcSMatthew Dillon return error;
53447370bffcSMatthew Dillon /* Set physical address of RX ring. */
53457370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_RX_BASE, sc->rxq.desc_dma.paddr);
53467370bffcSMatthew Dillon /* Set physical address of RX read pointer. */
53477370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_RX_RPTR_ADDR, sc->shared_dma.paddr +
53487370bffcSMatthew Dillon offsetof(struct wpi_shared, next));
53497370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_RX_WPTR, 0);
53507370bffcSMatthew Dillon /* Enable RX. */
53517370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_RX_CONFIG,
53527370bffcSMatthew Dillon WPI_FH_RX_CONFIG_DMA_ENA |
53537370bffcSMatthew Dillon WPI_FH_RX_CONFIG_RDRBD_ENA |
53547370bffcSMatthew Dillon WPI_FH_RX_CONFIG_WRSTATUS_ENA |
53557370bffcSMatthew Dillon WPI_FH_RX_CONFIG_MAXFRAG |
53567370bffcSMatthew Dillon WPI_FH_RX_CONFIG_NRBD(WPI_RX_RING_COUNT_LOG) |
53577370bffcSMatthew Dillon WPI_FH_RX_CONFIG_IRQ_DST_HOST |
53587370bffcSMatthew Dillon WPI_FH_RX_CONFIG_IRQ_TIMEOUT(1));
53597370bffcSMatthew Dillon (void)WPI_READ(sc, WPI_FH_RSSR_TBL); /* barrier */
53607370bffcSMatthew Dillon wpi_nic_unlock(sc);
53617370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_RX_WPTR, (WPI_RX_RING_COUNT - 1) & ~7);
53627370bffcSMatthew Dillon
53637370bffcSMatthew Dillon /* Initialize TX rings. */
53647370bffcSMatthew Dillon if ((error = wpi_nic_lock(sc)) != 0)
53657370bffcSMatthew Dillon return error;
53667370bffcSMatthew Dillon wpi_prph_write(sc, WPI_ALM_SCHED_MODE, 2); /* bypass mode */
53677370bffcSMatthew Dillon wpi_prph_write(sc, WPI_ALM_SCHED_ARASTAT, 1); /* enable RA0 */
53687370bffcSMatthew Dillon /* Enable all 6 TX rings. */
53697370bffcSMatthew Dillon wpi_prph_write(sc, WPI_ALM_SCHED_TXFACT, 0x3f);
53707370bffcSMatthew Dillon wpi_prph_write(sc, WPI_ALM_SCHED_SBYPASS_MODE1, 0x10000);
53717370bffcSMatthew Dillon wpi_prph_write(sc, WPI_ALM_SCHED_SBYPASS_MODE2, 0x30002);
53727370bffcSMatthew Dillon wpi_prph_write(sc, WPI_ALM_SCHED_TXF4MF, 4);
53737370bffcSMatthew Dillon wpi_prph_write(sc, WPI_ALM_SCHED_TXF5MF, 5);
53747370bffcSMatthew Dillon /* Set physical address of TX rings. */
53757370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_TX_BASE, sc->shared_dma.paddr);
53767370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_MSG_CONFIG, 0xffff05a5);
53777370bffcSMatthew Dillon
53787370bffcSMatthew Dillon /* Enable all DMA channels. */
53797370bffcSMatthew Dillon for (chnl = 0; chnl < WPI_NDMACHNLS; chnl++) {
53807370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_CBBC_CTRL(chnl), 0);
53817370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_CBBC_BASE(chnl), 0);
53827370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_TX_CONFIG(chnl), 0x80200008);
53837370bffcSMatthew Dillon }
53847370bffcSMatthew Dillon wpi_nic_unlock(sc);
53857370bffcSMatthew Dillon (void)WPI_READ(sc, WPI_FH_TX_BASE); /* barrier */
53867370bffcSMatthew Dillon
53877370bffcSMatthew Dillon /* Clear "radio off" and "commands blocked" bits. */
53887370bffcSMatthew Dillon WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL);
53897370bffcSMatthew Dillon WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_CMD_BLOCKED);
53907370bffcSMatthew Dillon
53917370bffcSMatthew Dillon /* Clear pending interrupts. */
53927370bffcSMatthew Dillon WPI_WRITE(sc, WPI_INT, 0xffffffff);
53937370bffcSMatthew Dillon /* Enable interrupts. */
53947370bffcSMatthew Dillon WPI_WRITE(sc, WPI_INT_MASK, WPI_INT_MASK_DEF);
53957370bffcSMatthew Dillon
53967370bffcSMatthew Dillon /* _Really_ make sure "radio off" bit is cleared! */
53977370bffcSMatthew Dillon WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL);
53987370bffcSMatthew Dillon WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL);
53997370bffcSMatthew Dillon
54007370bffcSMatthew Dillon if ((error = wpi_load_firmware(sc)) != 0) {
54017370bffcSMatthew Dillon device_printf(sc->sc_dev,
54027370bffcSMatthew Dillon "%s: could not load firmware, error %d\n", __func__,
54037370bffcSMatthew Dillon error);
54047370bffcSMatthew Dillon return error;
54057370bffcSMatthew Dillon }
54067370bffcSMatthew Dillon /* Wait at most one second for firmware alive notification. */
54077370bffcSMatthew Dillon #if defined(__DragonFly__)
54087370bffcSMatthew Dillon if ((error = lksleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) {
54097370bffcSMatthew Dillon #else
54107370bffcSMatthew Dillon if ((error = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) {
54117370bffcSMatthew Dillon #endif
54127370bffcSMatthew Dillon device_printf(sc->sc_dev,
54137370bffcSMatthew Dillon "%s: timeout waiting for adapter to initialize, error %d\n",
54147370bffcSMatthew Dillon __func__, error);
54157370bffcSMatthew Dillon return error;
54167370bffcSMatthew Dillon }
54177370bffcSMatthew Dillon
54187370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
54197370bffcSMatthew Dillon
54207370bffcSMatthew Dillon /* Do post-firmware initialization. */
54217370bffcSMatthew Dillon return wpi_post_alive(sc);
54227370bffcSMatthew Dillon }
54237370bffcSMatthew Dillon
54247370bffcSMatthew Dillon static void
54257370bffcSMatthew Dillon wpi_hw_stop(struct wpi_softc *sc)
54267370bffcSMatthew Dillon {
54277370bffcSMatthew Dillon uint8_t chnl, qid;
54287370bffcSMatthew Dillon int ntries;
54297370bffcSMatthew Dillon
54307370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
54317370bffcSMatthew Dillon
54327370bffcSMatthew Dillon if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP)
54337370bffcSMatthew Dillon wpi_nic_lock(sc);
54347370bffcSMatthew Dillon
54357370bffcSMatthew Dillon WPI_WRITE(sc, WPI_RESET, WPI_RESET_NEVO);
54367370bffcSMatthew Dillon
54377370bffcSMatthew Dillon /* Disable interrupts. */
54387370bffcSMatthew Dillon WPI_WRITE(sc, WPI_INT_MASK, 0);
54397370bffcSMatthew Dillon WPI_WRITE(sc, WPI_INT, 0xffffffff);
54407370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_INT, 0xffffffff);
54417370bffcSMatthew Dillon
54427370bffcSMatthew Dillon /* Make sure we no longer hold the NIC lock. */
54437370bffcSMatthew Dillon wpi_nic_unlock(sc);
54447370bffcSMatthew Dillon
54457370bffcSMatthew Dillon if (wpi_nic_lock(sc) == 0) {
54467370bffcSMatthew Dillon /* Stop TX scheduler. */
54477370bffcSMatthew Dillon wpi_prph_write(sc, WPI_ALM_SCHED_MODE, 0);
54487370bffcSMatthew Dillon wpi_prph_write(sc, WPI_ALM_SCHED_TXFACT, 0);
54497370bffcSMatthew Dillon
54507370bffcSMatthew Dillon /* Stop all DMA channels. */
54517370bffcSMatthew Dillon for (chnl = 0; chnl < WPI_NDMACHNLS; chnl++) {
54527370bffcSMatthew Dillon WPI_WRITE(sc, WPI_FH_TX_CONFIG(chnl), 0);
54537370bffcSMatthew Dillon for (ntries = 0; ntries < 200; ntries++) {
54547370bffcSMatthew Dillon if (WPI_READ(sc, WPI_FH_TX_STATUS) &
54557370bffcSMatthew Dillon WPI_FH_TX_STATUS_IDLE(chnl))
54567370bffcSMatthew Dillon break;
54577370bffcSMatthew Dillon DELAY(10);
54587370bffcSMatthew Dillon }
54597370bffcSMatthew Dillon }
54607370bffcSMatthew Dillon wpi_nic_unlock(sc);
54617370bffcSMatthew Dillon }
54627370bffcSMatthew Dillon
54637370bffcSMatthew Dillon /* Stop RX ring. */
54647370bffcSMatthew Dillon wpi_reset_rx_ring(sc);
54657370bffcSMatthew Dillon
54667370bffcSMatthew Dillon /* Reset all TX rings. */
54677370bffcSMatthew Dillon for (qid = 0; qid < WPI_DRV_NTXQUEUES; qid++)
54687370bffcSMatthew Dillon wpi_reset_tx_ring(sc, &sc->txq[qid]);
54697370bffcSMatthew Dillon
54707370bffcSMatthew Dillon if (wpi_nic_lock(sc) == 0) {
54717370bffcSMatthew Dillon wpi_prph_write(sc, WPI_APMG_CLK_DIS,
54727370bffcSMatthew Dillon WPI_APMG_CLK_CTRL_DMA_CLK_RQT);
54737370bffcSMatthew Dillon wpi_nic_unlock(sc);
54747370bffcSMatthew Dillon }
54757370bffcSMatthew Dillon DELAY(5);
54767370bffcSMatthew Dillon /* Power OFF adapter. */
54777370bffcSMatthew Dillon wpi_apm_stop(sc);
54787370bffcSMatthew Dillon }
54797370bffcSMatthew Dillon
54807370bffcSMatthew Dillon static void
54817370bffcSMatthew Dillon wpi_radio_on(void *arg0, int pending)
54827370bffcSMatthew Dillon {
54837370bffcSMatthew Dillon struct wpi_softc *sc = arg0;
54847370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
54857370bffcSMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
54867370bffcSMatthew Dillon
54877370bffcSMatthew Dillon device_printf(sc->sc_dev, "RF switch: radio enabled\n");
54887370bffcSMatthew Dillon
54897370bffcSMatthew Dillon WPI_LOCK(sc);
54907370bffcSMatthew Dillon callout_stop(&sc->watchdog_rfkill);
54917370bffcSMatthew Dillon WPI_UNLOCK(sc);
54927370bffcSMatthew Dillon
54937370bffcSMatthew Dillon if (vap != NULL)
54947370bffcSMatthew Dillon ieee80211_init(vap);
54957370bffcSMatthew Dillon }
54967370bffcSMatthew Dillon
54977370bffcSMatthew Dillon static void
54987370bffcSMatthew Dillon wpi_radio_off(void *arg0, int pending)
54997370bffcSMatthew Dillon {
55007370bffcSMatthew Dillon struct wpi_softc *sc = arg0;
55017370bffcSMatthew Dillon struct ieee80211com *ic = &sc->sc_ic;
55027370bffcSMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
55037370bffcSMatthew Dillon
55047370bffcSMatthew Dillon device_printf(sc->sc_dev, "RF switch: radio disabled\n");
55057370bffcSMatthew Dillon
55067370bffcSMatthew Dillon ieee80211_notify_radio(ic, 0);
55077370bffcSMatthew Dillon wpi_stop(sc);
55087370bffcSMatthew Dillon if (vap != NULL)
55097370bffcSMatthew Dillon ieee80211_stop(vap);
55107370bffcSMatthew Dillon
55117370bffcSMatthew Dillon WPI_LOCK(sc);
55127370bffcSMatthew Dillon callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, sc);
55137370bffcSMatthew Dillon WPI_UNLOCK(sc);
55147370bffcSMatthew Dillon }
55157370bffcSMatthew Dillon
55167370bffcSMatthew Dillon static int
55177370bffcSMatthew Dillon wpi_init(struct wpi_softc *sc)
55187370bffcSMatthew Dillon {
55197370bffcSMatthew Dillon int error = 0;
55207370bffcSMatthew Dillon
55217370bffcSMatthew Dillon WPI_LOCK(sc);
55227370bffcSMatthew Dillon
55237370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__);
55247370bffcSMatthew Dillon
55257370bffcSMatthew Dillon if (sc->sc_running != 0)
55267370bffcSMatthew Dillon goto end;
55277370bffcSMatthew Dillon
55287370bffcSMatthew Dillon /* Check that the radio is not disabled by hardware switch. */
55297370bffcSMatthew Dillon if (!(WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_RFKILL)) {
55307370bffcSMatthew Dillon device_printf(sc->sc_dev,
55317370bffcSMatthew Dillon "RF switch: radio disabled (%s)\n", __func__);
55327370bffcSMatthew Dillon callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill,
55337370bffcSMatthew Dillon sc);
55347370bffcSMatthew Dillon error = EINPROGRESS;
55357370bffcSMatthew Dillon goto end;
55367370bffcSMatthew Dillon }
55377370bffcSMatthew Dillon
55387370bffcSMatthew Dillon /* Read firmware images from the filesystem. */
55397370bffcSMatthew Dillon if ((error = wpi_read_firmware(sc)) != 0) {
55407370bffcSMatthew Dillon device_printf(sc->sc_dev,
55417370bffcSMatthew Dillon "%s: could not read firmware, error %d\n", __func__,
55427370bffcSMatthew Dillon error);
55437370bffcSMatthew Dillon goto end;
55447370bffcSMatthew Dillon }
55457370bffcSMatthew Dillon
55467370bffcSMatthew Dillon sc->sc_running = 1;
55477370bffcSMatthew Dillon
55487370bffcSMatthew Dillon /* Initialize hardware and upload firmware. */
55497370bffcSMatthew Dillon error = wpi_hw_init(sc);
55507370bffcSMatthew Dillon wpi_unload_firmware(sc);
55517370bffcSMatthew Dillon if (error != 0) {
55527370bffcSMatthew Dillon device_printf(sc->sc_dev,
55537370bffcSMatthew Dillon "%s: could not initialize hardware, error %d\n", __func__,
55547370bffcSMatthew Dillon error);
55557370bffcSMatthew Dillon goto fail;
55567370bffcSMatthew Dillon }
55577370bffcSMatthew Dillon
55587370bffcSMatthew Dillon /* Configure adapter now that it is ready. */
55597370bffcSMatthew Dillon if ((error = wpi_config(sc)) != 0) {
55607370bffcSMatthew Dillon device_printf(sc->sc_dev,
55617370bffcSMatthew Dillon "%s: could not configure device, error %d\n", __func__,
55627370bffcSMatthew Dillon error);
55637370bffcSMatthew Dillon goto fail;
55647370bffcSMatthew Dillon }
55657370bffcSMatthew Dillon
55667370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__);
55677370bffcSMatthew Dillon
55687370bffcSMatthew Dillon WPI_UNLOCK(sc);
55697370bffcSMatthew Dillon
55707370bffcSMatthew Dillon return 0;
55717370bffcSMatthew Dillon
55727370bffcSMatthew Dillon fail: wpi_stop_locked(sc);
55737370bffcSMatthew Dillon
55747370bffcSMatthew Dillon end: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__);
55757370bffcSMatthew Dillon WPI_UNLOCK(sc);
55767370bffcSMatthew Dillon
55777370bffcSMatthew Dillon return error;
55787370bffcSMatthew Dillon }
55797370bffcSMatthew Dillon
55807370bffcSMatthew Dillon static void
55817370bffcSMatthew Dillon wpi_stop_locked(struct wpi_softc *sc)
55827370bffcSMatthew Dillon {
55837370bffcSMatthew Dillon
55847370bffcSMatthew Dillon WPI_LOCK_ASSERT(sc);
55857370bffcSMatthew Dillon
55867370bffcSMatthew Dillon if (sc->sc_running == 0)
55877370bffcSMatthew Dillon return;
55887370bffcSMatthew Dillon
55897370bffcSMatthew Dillon WPI_TX_LOCK(sc);
55907370bffcSMatthew Dillon WPI_TXQ_LOCK(sc);
55917370bffcSMatthew Dillon sc->sc_running = 0;
55927370bffcSMatthew Dillon WPI_TXQ_UNLOCK(sc);
55937370bffcSMatthew Dillon WPI_TX_UNLOCK(sc);
55947370bffcSMatthew Dillon
55957370bffcSMatthew Dillon WPI_TXQ_STATE_LOCK(sc);
55967370bffcSMatthew Dillon callout_stop(&sc->tx_timeout);
55977370bffcSMatthew Dillon WPI_TXQ_STATE_UNLOCK(sc);
55987370bffcSMatthew Dillon
55997370bffcSMatthew Dillon WPI_RXON_LOCK(sc);
56007370bffcSMatthew Dillon callout_stop(&sc->scan_timeout);
56017370bffcSMatthew Dillon callout_stop(&sc->calib_to);
56027370bffcSMatthew Dillon WPI_RXON_UNLOCK(sc);
56037370bffcSMatthew Dillon
56047370bffcSMatthew Dillon /* Power OFF hardware. */
56057370bffcSMatthew Dillon wpi_hw_stop(sc);
56067370bffcSMatthew Dillon }
56077370bffcSMatthew Dillon
56087370bffcSMatthew Dillon static void
56097370bffcSMatthew Dillon wpi_stop(struct wpi_softc *sc)
56107370bffcSMatthew Dillon {
56117370bffcSMatthew Dillon WPI_LOCK(sc);
56127370bffcSMatthew Dillon wpi_stop_locked(sc);
56137370bffcSMatthew Dillon WPI_UNLOCK(sc);
56147370bffcSMatthew Dillon }
56157370bffcSMatthew Dillon
56167370bffcSMatthew Dillon /*
56177370bffcSMatthew Dillon * Callback from net80211 to start a scan.
56184db7dd1bSJoe Talbott */
56194db7dd1bSJoe Talbott static void
56204db7dd1bSJoe Talbott wpi_scan_start(struct ieee80211com *ic)
56214db7dd1bSJoe Talbott {
56224f1aaf2fSImre Vadász struct wpi_softc *sc = ic->ic_softc;
56234db7dd1bSJoe Talbott
56244db7dd1bSJoe Talbott wpi_set_led(sc, WPI_LED_LINK, 20, 2);
56254db7dd1bSJoe Talbott }
56264db7dd1bSJoe Talbott
56277370bffcSMatthew Dillon /*
56287370bffcSMatthew Dillon * Callback from net80211 to terminate a scan.
56294db7dd1bSJoe Talbott */
56304db7dd1bSJoe Talbott static void
56314db7dd1bSJoe Talbott wpi_scan_end(struct ieee80211com *ic)
56324db7dd1bSJoe Talbott {
56337370bffcSMatthew Dillon struct wpi_softc *sc = ic->ic_softc;
56347370bffcSMatthew Dillon struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
56357370bffcSMatthew Dillon
56367370bffcSMatthew Dillon if (vap->iv_state == IEEE80211_S_RUN)
56377370bffcSMatthew Dillon wpi_set_led(sc, WPI_LED_LINK, 0, 1);
56384db7dd1bSJoe Talbott }
56394db7dd1bSJoe Talbott
56404db7dd1bSJoe Talbott /**
56414db7dd1bSJoe Talbott * Called by the net80211 framework to indicate to the driver
56424db7dd1bSJoe Talbott * that the channel should be changed
56434db7dd1bSJoe Talbott */
56444db7dd1bSJoe Talbott static void
56454db7dd1bSJoe Talbott wpi_set_channel(struct ieee80211com *ic)
56464db7dd1bSJoe Talbott {
56477370bffcSMatthew Dillon const struct ieee80211_channel *c = ic->ic_curchan;
56484f1aaf2fSImre Vadász struct wpi_softc *sc = ic->ic_softc;
56494db7dd1bSJoe Talbott int error;
56504db7dd1bSJoe Talbott
56517370bffcSMatthew Dillon DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__);
56527370bffcSMatthew Dillon
56537370bffcSMatthew Dillon WPI_LOCK(sc);
56547370bffcSMatthew Dillon sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq);
56557370bffcSMatthew Dillon sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags);
56567370bffcSMatthew Dillon WPI_UNLOCK(sc);
56577370bffcSMatthew Dillon WPI_TX_LOCK(sc);
56587370bffcSMatthew Dillon sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq);
56597370bffcSMatthew Dillon sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags);
56607370bffcSMatthew Dillon WPI_TX_UNLOCK(sc);
56617370bffcSMatthew Dillon
56624db7dd1bSJoe Talbott /*
56634db7dd1bSJoe Talbott * Only need to set the channel in Monitor mode. AP scanning and auth
56644db7dd1bSJoe Talbott * are already taken care of by their respective firmware commands.
56654db7dd1bSJoe Talbott */
56664db7dd1bSJoe Talbott if (ic->ic_opmode == IEEE80211_M_MONITOR) {
56677370bffcSMatthew Dillon WPI_RXON_LOCK(sc);
56687370bffcSMatthew Dillon sc->rxon.chan = ieee80211_chan2ieee(ic, c);
56697370bffcSMatthew Dillon if (IEEE80211_IS_CHAN_2GHZ(c)) {
56707370bffcSMatthew Dillon sc->rxon.flags |= htole32(WPI_RXON_AUTO |
56717370bffcSMatthew Dillon WPI_RXON_24GHZ);
56727370bffcSMatthew Dillon } else {
56737370bffcSMatthew Dillon sc->rxon.flags &= ~htole32(WPI_RXON_AUTO |
56747370bffcSMatthew Dillon WPI_RXON_24GHZ);
56757370bffcSMatthew Dillon }
56767370bffcSMatthew Dillon if ((error = wpi_send_rxon(sc, 0, 1)) != 0)
56774db7dd1bSJoe Talbott device_printf(sc->sc_dev,
56787370bffcSMatthew Dillon "%s: error %d setting channel\n", __func__,
56797370bffcSMatthew Dillon error);
56807370bffcSMatthew Dillon WPI_RXON_UNLOCK(sc);
56814db7dd1bSJoe Talbott }
56824db7dd1bSJoe Talbott }
56834db7dd1bSJoe Talbott
56844db7dd1bSJoe Talbott /**
56854db7dd1bSJoe Talbott * Called by net80211 to indicate that we need to scan the current
56864db7dd1bSJoe Talbott * channel. The channel is previously be set via the wpi_set_channel
56874db7dd1bSJoe Talbott * callback.
56884db7dd1bSJoe Talbott */
56894db7dd1bSJoe Talbott static void
56904db7dd1bSJoe Talbott wpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
56914db7dd1bSJoe Talbott {
56924db7dd1bSJoe Talbott struct ieee80211vap *vap = ss->ss_vap;
56934f1aaf2fSImre Vadász struct ieee80211com *ic = vap->iv_ic;
56944f1aaf2fSImre Vadász struct wpi_softc *sc = ic->ic_softc;
56957370bffcSMatthew Dillon int error;
56964db7dd1bSJoe Talbott
56977370bffcSMatthew Dillon WPI_RXON_LOCK(sc);
56987370bffcSMatthew Dillon error = wpi_scan(sc, ic->ic_curchan);
56997370bffcSMatthew Dillon WPI_RXON_UNLOCK(sc);
57007370bffcSMatthew Dillon if (error != 0)
57014db7dd1bSJoe Talbott ieee80211_cancel_scan(vap);
57024db7dd1bSJoe Talbott }
57034db7dd1bSJoe Talbott
57044db7dd1bSJoe Talbott /**
57054db7dd1bSJoe Talbott * Called by the net80211 framework to indicate
57064db7dd1bSJoe Talbott * the minimum dwell time has been met, terminate the scan.
57074db7dd1bSJoe Talbott * We don't actually terminate the scan as the firmware will notify
57084db7dd1bSJoe Talbott * us when it's finished and we have no way to interrupt it.
57094db7dd1bSJoe Talbott */
57104db7dd1bSJoe Talbott static void
57114db7dd1bSJoe Talbott wpi_scan_mindwell(struct ieee80211_scan_state *ss)
57124db7dd1bSJoe Talbott {
57134db7dd1bSJoe Talbott /* NB: don't try to abort scan; wait for firmware to finish */
57144db7dd1bSJoe Talbott }
5715