1139749Simp/*- 2123474Swpaul * Copyright (c) 2003 3123474Swpaul * Bill Paul <wpaul@windriver.com>. All rights reserved. 4123474Swpaul * 5123474Swpaul * Redistribution and use in source and binary forms, with or without 6123474Swpaul * modification, are permitted provided that the following conditions 7123474Swpaul * are met: 8123474Swpaul * 1. Redistributions of source code must retain the above copyright 9123474Swpaul * notice, this list of conditions and the following disclaimer. 10123474Swpaul * 2. Redistributions in binary form must reproduce the above copyright 11123474Swpaul * notice, this list of conditions and the following disclaimer in the 12123474Swpaul * documentation and/or other materials provided with the distribution. 13123474Swpaul * 3. All advertising materials mentioning features or use of this software 14123474Swpaul * must display the following acknowledgement: 15123474Swpaul * This product includes software developed by Bill Paul. 16123474Swpaul * 4. Neither the name of the author nor the names of any co-contributors 17123474Swpaul * may be used to endorse or promote products derived from this software 18123474Swpaul * without specific prior written permission. 19123474Swpaul * 20123474Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21123474Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22123474Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23123474Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 24123474Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25123474Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26123474Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27123474Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28123474Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29123474Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30123474Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 31145283Swpaul * 32151207Swpaul * WPA support originally contributed by Arvind Srinivasan <arvind@celar.us> 33151207Swpaul * then hacked upon mercilessly by my. 34123474Swpaul */ 35123474Swpaul 36123474Swpaul#include <sys/cdefs.h> 37123474Swpaul__FBSDID("$FreeBSD: stable/10/sys/dev/if_ndis/if_ndis.c 343820 2019-02-06 02:07:37Z avos $"); 38123474Swpaul 39123474Swpaul#include <sys/param.h> 40123474Swpaul#include <sys/systm.h> 41123474Swpaul#include <sys/sockio.h> 42123474Swpaul#include <sys/mbuf.h> 43123474Swpaul#include <sys/malloc.h> 44171390Sthompsa#include <sys/endian.h> 45164033Srwatson#include <sys/priv.h> 46123474Swpaul#include <sys/kernel.h> 47123474Swpaul#include <sys/socket.h> 48123474Swpaul#include <sys/queue.h> 49141524Swpaul#include <sys/module.h> 50143204Swpaul#include <sys/proc.h> 51123474Swpaul#include <sys/sysctl.h> 52171390Sthompsa#include <sys/kthread.h> 53123474Swpaul 54123474Swpaul#include <net/if.h> 55123474Swpaul#include <net/if_arp.h> 56123474Swpaul#include <net/ethernet.h> 57123474Swpaul#include <net/if_dl.h> 58123474Swpaul#include <net/if_media.h> 59147256Sbrooks#include <net/if_types.h> 60129002Sandre#include <net/route.h> 61123474Swpaul 62123474Swpaul#include <net/bpf.h> 63123474Swpaul 64123474Swpaul#include <machine/bus.h> 65123474Swpaul#include <machine/resource.h> 66123474Swpaul#include <sys/bus.h> 67123474Swpaul#include <sys/rman.h> 68123474Swpaul 69123695Swpaul#include <net80211/ieee80211_var.h> 70123695Swpaul#include <net80211/ieee80211_ioctl.h> 71171390Sthompsa#include <net80211/ieee80211_regdomain.h> 72123695Swpaul 73123474Swpaul#include <dev/pci/pcireg.h> 74123474Swpaul#include <dev/pci/pcivar.h> 75189488Sweongyo#include <dev/usb/usb.h> 76194677Sthompsa#include <dev/usb/usbdi.h> 77123474Swpaul 78123474Swpaul#include <compat/ndis/pe_var.h> 79145485Swpaul#include <compat/ndis/cfg_var.h> 80123474Swpaul#include <compat/ndis/resource_var.h> 81132953Swpaul#include <compat/ndis/ntoskrnl_var.h> 82128229Swpaul#include <compat/ndis/hal_var.h> 83123474Swpaul#include <compat/ndis/ndis_var.h> 84186507Sweongyo#include <compat/ndis/usbd_var.h> 85123474Swpaul#include <dev/if_ndis/if_ndisvar.h> 86123474Swpaul 87171602Sthompsa#define NDIS_DEBUG 88171390Sthompsa#ifdef NDIS_DEBUG 89171602Sthompsa#define DPRINTF(x) do { if (ndis_debug > 0) printf x; } while (0) 90171602Sthompsaint ndis_debug = 0; 91171602SthompsaSYSCTL_INT(_debug, OID_AUTO, ndis, CTLFLAG_RW, &ndis_debug, 0, 92171602Sthompsa "if_ndis debug level"); 93171390Sthompsa#else 94171390Sthompsa#define DPRINTF(x) 95171390Sthompsa#endif 96171390Sthompsa 97186507SweongyoSYSCTL_DECL(_hw_ndisusb); 98186507Sweongyoint ndisusb_halt = 1; 99186507SweongyoSYSCTL_INT(_hw_ndisusb, OID_AUTO, halt, CTLFLAG_RW, &ndisusb_halt, 0, 100186507Sweongyo "Halt NDIS USB driver when it's attached"); 101186507Sweongyo 102187104Sthompsa/* 0 - 30 dBm to mW conversion table */ 103189550Ssamstatic const uint16_t dBm2mW[] = { 104187104Sthompsa 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 105187104Sthompsa 3, 4, 4, 4, 5, 6, 6, 7, 8, 9, 106187104Sthompsa 10, 11, 13, 14, 16, 18, 20, 22, 25, 28, 107187104Sthompsa 32, 35, 40, 45, 50, 56, 63, 71, 79, 89, 108187104Sthompsa 100, 112, 126, 141, 158, 178, 200, 224, 251, 282, 109187104Sthompsa 316, 355, 398, 447, 501, 562, 631, 708, 794, 891, 110187104Sthompsa 1000 111187104Sthompsa}; 112187104Sthompsa 113145485SwpaulMODULE_DEPEND(ndis, ether, 1, 1, 1); 114145485SwpaulMODULE_DEPEND(ndis, wlan, 1, 1, 1); 115145485SwpaulMODULE_DEPEND(ndis, ndisapi, 1, 1, 1); 116126706Swpaul 117145485SwpaulMODULE_VERSION(ndis, 1); 118123474Swpaul 119126706Swpaulint ndis_attach (device_t); 120126706Swpaulint ndis_detach (device_t); 121126706Swpaulint ndis_suspend (device_t); 122126706Swpaulint ndis_resume (device_t); 123126706Swpaulvoid ndis_shutdown (device_t); 124123474Swpaul 125141524Swpaulint ndisdrv_modevent (module_t, int, void *); 126141524Swpaul 127144888Swpaulstatic void ndis_txeof (ndis_handle, ndis_packet *, ndis_status); 128144888Swpaulstatic void ndis_rxeof (ndis_handle, ndis_packet **, uint32_t); 129146230Swpaulstatic void ndis_rxeof_eth (ndis_handle, ndis_handle, char *, void *, 130146230Swpaul uint32_t, void *, uint32_t, uint32_t); 131146230Swpaulstatic void ndis_rxeof_done (ndis_handle); 132146230Swpaulstatic void ndis_rxeof_xfr (kdpc *, ndis_handle, void *, void *); 133146230Swpaulstatic void ndis_rxeof_xfr_done (ndis_handle, ndis_packet *, 134146230Swpaul uint32_t, uint32_t); 135144888Swpaulstatic void ndis_linksts (ndis_handle, ndis_status, void *, uint32_t); 136144888Swpaulstatic void ndis_linksts_done (ndis_handle); 137123695Swpaul 138141963Swpaul/* We need to wrap these functions for amd64. */ 139141963Swpaulstatic funcptr ndis_txeof_wrap; 140141963Swpaulstatic funcptr ndis_rxeof_wrap; 141146230Swpaulstatic funcptr ndis_rxeof_eth_wrap; 142146230Swpaulstatic funcptr ndis_rxeof_done_wrap; 143146230Swpaulstatic funcptr ndis_rxeof_xfr_wrap; 144146230Swpaulstatic funcptr ndis_rxeof_xfr_done_wrap; 145141963Swpaulstatic funcptr ndis_linksts_wrap; 146141963Swpaulstatic funcptr ndis_linksts_done_wrap; 147145895Swpaulstatic funcptr ndis_ticktask_wrap; 148145895Swpaulstatic funcptr ndis_starttask_wrap; 149145895Swpaulstatic funcptr ndis_resettask_wrap; 150151451Swpaulstatic funcptr ndis_inputtask_wrap; 151141963Swpaul 152178354Ssamstatic struct ieee80211vap *ndis_vap_create(struct ieee80211com *, 153228621Sbschmidt const char [IFNAMSIZ], int, enum ieee80211_opmode, int, 154228621Sbschmidt const uint8_t [IEEE80211_ADDR_LEN], 155228621Sbschmidt const uint8_t [IEEE80211_ADDR_LEN]); 156178354Ssamstatic void ndis_vap_delete (struct ieee80211vap *); 157123474Swpaulstatic void ndis_tick (void *); 158151207Swpaulstatic void ndis_ticktask (device_object *, void *); 159178354Ssamstatic int ndis_raw_xmit (struct ieee80211_node *, struct mbuf *, 160178354Ssam const struct ieee80211_bpf_params *); 161185485Ssamstatic void ndis_update_mcast (struct ifnet *ifp); 162185485Ssamstatic void ndis_update_promisc (struct ifnet *ifp); 163123474Swpaulstatic void ndis_start (struct ifnet *); 164151207Swpaulstatic void ndis_starttask (device_object *, void *); 165151207Swpaulstatic void ndis_resettask (device_object *, void *); 166151451Swpaulstatic void ndis_inputtask (device_object *, void *); 167123474Swpaulstatic int ndis_ioctl (struct ifnet *, u_long, caddr_t); 168178704Sthompsastatic int ndis_ioctl_80211 (struct ifnet *, u_long, caddr_t); 169178354Ssamstatic int ndis_newstate (struct ieee80211vap *, enum ieee80211_state, 170171390Sthompsa int); 171171602Sthompsastatic int ndis_nettype_chan (uint32_t); 172171602Sthompsastatic int ndis_nettype_mode (uint32_t); 173191746Sthompsastatic void ndis_scan (void *); 174171390Sthompsastatic void ndis_scan_results (struct ndis_softc *); 175171390Sthompsastatic void ndis_scan_start (struct ieee80211com *); 176171390Sthompsastatic void ndis_scan_end (struct ieee80211com *); 177171390Sthompsastatic void ndis_set_channel (struct ieee80211com *); 178178354Ssamstatic void ndis_scan_curchan (struct ieee80211_scan_state *, unsigned long); 179178354Ssamstatic void ndis_scan_mindwell (struct ieee80211_scan_state *); 180123474Swpaulstatic void ndis_init (void *); 181123474Swpaulstatic void ndis_stop (struct ndis_softc *); 182123474Swpaulstatic int ndis_ifmedia_upd (struct ifnet *); 183123474Swpaulstatic void ndis_ifmedia_sts (struct ifnet *, struct ifmediareq *); 184194706Scokanestatic int ndis_get_bssid_list (struct ndis_softc *, 185194706Scokane ndis_80211_bssid_list_ex **); 186127349Swpaulstatic int ndis_get_assoc (struct ndis_softc *, ndis_wlan_bssid_ex **); 187124821Swpaulstatic int ndis_probe_offload (struct ndis_softc *); 188124821Swpaulstatic int ndis_set_offload (struct ndis_softc *); 189123695Swpaulstatic void ndis_getstate_80211 (struct ndis_softc *); 190123695Swpaulstatic void ndis_setstate_80211 (struct ndis_softc *); 191178929Sthompsastatic void ndis_auth_and_assoc (struct ndis_softc *, struct ieee80211vap *); 192201620Srpaulostatic void ndis_media_status (struct ifnet *, struct ifmediareq *); 193151207Swpaulstatic int ndis_set_cipher (struct ndis_softc *, int); 194178354Ssamstatic int ndis_set_wpa (struct ndis_softc *, void *, int); 195178354Ssamstatic int ndis_add_key (struct ieee80211vap *, 196151207Swpaul const struct ieee80211_key *, const u_int8_t []); 197178354Ssamstatic int ndis_del_key (struct ieee80211vap *, 198151207Swpaul const struct ieee80211_key *); 199123474Swpaul 200123474Swpaulstatic void ndis_setmulti (struct ndis_softc *); 201123474Swpaulstatic void ndis_map_sclist (void *, bus_dma_segment_t *, 202123474Swpaul int, bus_size_t, int); 203123474Swpaul 204141524Swpaulstatic int ndisdrv_loaded = 0; 205123474Swpaul 206123474Swpaul/* 207141524Swpaul * This routine should call windrv_load() once for each driver 208141524Swpaul * image. This will do the relocation and dynalinking for the 209141524Swpaul * image, and create a Windows driver object which will be 210141524Swpaul * saved in our driver database. 211141524Swpaul */ 212141524Swpaulint 213141524Swpaulndisdrv_modevent(mod, cmd, arg) 214141524Swpaul module_t mod; 215141524Swpaul int cmd; 216141524Swpaul void *arg; 217141524Swpaul{ 218141524Swpaul int error = 0; 219141524Swpaul 220141524Swpaul switch (cmd) { 221141524Swpaul case MOD_LOAD: 222141524Swpaul ndisdrv_loaded++; 223141524Swpaul if (ndisdrv_loaded > 1) 224141524Swpaul break; 225144888Swpaul windrv_wrap((funcptr)ndis_rxeof, &ndis_rxeof_wrap, 226144888Swpaul 3, WINDRV_WRAP_STDCALL); 227146230Swpaul windrv_wrap((funcptr)ndis_rxeof_eth, &ndis_rxeof_eth_wrap, 228146230Swpaul 8, WINDRV_WRAP_STDCALL); 229146230Swpaul windrv_wrap((funcptr)ndis_rxeof_done, &ndis_rxeof_done_wrap, 230146230Swpaul 1, WINDRV_WRAP_STDCALL); 231146230Swpaul windrv_wrap((funcptr)ndis_rxeof_xfr, &ndis_rxeof_xfr_wrap, 232146230Swpaul 4, WINDRV_WRAP_STDCALL); 233146230Swpaul windrv_wrap((funcptr)ndis_rxeof_xfr_done, 234146230Swpaul &ndis_rxeof_xfr_done_wrap, 4, WINDRV_WRAP_STDCALL); 235144888Swpaul windrv_wrap((funcptr)ndis_txeof, &ndis_txeof_wrap, 236144888Swpaul 3, WINDRV_WRAP_STDCALL); 237144888Swpaul windrv_wrap((funcptr)ndis_linksts, &ndis_linksts_wrap, 238144888Swpaul 4, WINDRV_WRAP_STDCALL); 239141963Swpaul windrv_wrap((funcptr)ndis_linksts_done, 240144888Swpaul &ndis_linksts_done_wrap, 1, WINDRV_WRAP_STDCALL); 241145895Swpaul windrv_wrap((funcptr)ndis_ticktask, &ndis_ticktask_wrap, 242145895Swpaul 2, WINDRV_WRAP_STDCALL); 243145895Swpaul windrv_wrap((funcptr)ndis_starttask, &ndis_starttask_wrap, 244145895Swpaul 2, WINDRV_WRAP_STDCALL); 245145895Swpaul windrv_wrap((funcptr)ndis_resettask, &ndis_resettask_wrap, 246145895Swpaul 2, WINDRV_WRAP_STDCALL); 247151451Swpaul windrv_wrap((funcptr)ndis_inputtask, &ndis_inputtask_wrap, 248151451Swpaul 2, WINDRV_WRAP_STDCALL); 249141524Swpaul break; 250141524Swpaul case MOD_UNLOAD: 251141524Swpaul ndisdrv_loaded--; 252141524Swpaul if (ndisdrv_loaded > 0) 253141524Swpaul break; 254145895Swpaul /* fallthrough */ 255141524Swpaul case MOD_SHUTDOWN: 256141963Swpaul windrv_unwrap(ndis_rxeof_wrap); 257146230Swpaul windrv_unwrap(ndis_rxeof_eth_wrap); 258146230Swpaul windrv_unwrap(ndis_rxeof_done_wrap); 259146230Swpaul windrv_unwrap(ndis_rxeof_xfr_wrap); 260146230Swpaul windrv_unwrap(ndis_rxeof_xfr_done_wrap); 261141963Swpaul windrv_unwrap(ndis_txeof_wrap); 262141963Swpaul windrv_unwrap(ndis_linksts_wrap); 263141963Swpaul windrv_unwrap(ndis_linksts_done_wrap); 264145895Swpaul windrv_unwrap(ndis_ticktask_wrap); 265145895Swpaul windrv_unwrap(ndis_starttask_wrap); 266145895Swpaul windrv_unwrap(ndis_resettask_wrap); 267151451Swpaul windrv_unwrap(ndis_inputtask_wrap); 268141524Swpaul break; 269141524Swpaul default: 270141524Swpaul error = EINVAL; 271141524Swpaul break; 272141524Swpaul } 273141524Swpaul 274141524Swpaul return (error); 275141524Swpaul} 276141524Swpaul 277141524Swpaul/* 278123474Swpaul * Program the 64-bit multicast hash filter. 279123474Swpaul */ 280123474Swpaulstatic void 281123474Swpaulndis_setmulti(sc) 282123474Swpaul struct ndis_softc *sc; 283123474Swpaul{ 284124709Swpaul struct ifnet *ifp; 285124709Swpaul struct ifmultiaddr *ifma; 286124709Swpaul int len, mclistsz, error; 287124709Swpaul uint8_t *mclist; 288124709Swpaul 289147256Sbrooks ifp = sc->ifp; 290124709Swpaul 291131750Swpaul if (!NDIS_INITIALIZED(sc)) 292124709Swpaul return; 293124709Swpaul 294124709Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 295124709Swpaul sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; 296124709Swpaul len = sizeof(sc->ndis_filter); 297124709Swpaul error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, 298124709Swpaul &sc->ndis_filter, &len); 299124709Swpaul if (error) 300198786Srpaulo device_printf(sc->ndis_dev, 301151207Swpaul "set allmulti failed: %d\n", error); 302124709Swpaul return; 303124709Swpaul } 304124709Swpaul 305127552Swpaul if (TAILQ_EMPTY(&ifp->if_multiaddrs)) 306127552Swpaul return; 307124709Swpaul 308124709Swpaul len = sizeof(mclistsz); 309124709Swpaul ndis_get_info(sc, OID_802_3_MAXIMUM_LIST_SIZE, &mclistsz, &len); 310124709Swpaul 311125061Swpaul mclist = malloc(ETHER_ADDR_LEN * mclistsz, M_TEMP, M_NOWAIT|M_ZERO); 312124709Swpaul 313124709Swpaul if (mclist == NULL) { 314124709Swpaul sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; 315124709Swpaul goto out; 316124709Swpaul } 317124709Swpaul 318124709Swpaul sc->ndis_filter |= NDIS_PACKET_TYPE_MULTICAST; 319124709Swpaul 320124709Swpaul len = 0; 321195049Srwatson if_maddr_rlock(ifp); 322124709Swpaul TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 323124709Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 324124709Swpaul continue; 325124709Swpaul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 326124709Swpaul mclist + (ETHER_ADDR_LEN * len), ETHER_ADDR_LEN); 327124709Swpaul len++; 328124709Swpaul if (len > mclistsz) { 329195049Srwatson if_maddr_runlock(ifp); 330124709Swpaul sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; 331124709Swpaul sc->ndis_filter &= ~NDIS_PACKET_TYPE_MULTICAST; 332124709Swpaul goto out; 333124709Swpaul } 334124709Swpaul } 335195049Srwatson if_maddr_runlock(ifp); 336124709Swpaul 337124709Swpaul len = len * ETHER_ADDR_LEN; 338124709Swpaul error = ndis_set_info(sc, OID_802_3_MULTICAST_LIST, mclist, &len); 339124709Swpaul if (error) { 340198786Srpaulo device_printf(sc->ndis_dev, "set mclist failed: %d\n", error); 341124709Swpaul sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; 342124709Swpaul sc->ndis_filter &= ~NDIS_PACKET_TYPE_MULTICAST; 343124709Swpaul } 344124709Swpaul 345124709Swpaulout: 346124709Swpaul free(mclist, M_TEMP); 347124709Swpaul 348124709Swpaul len = sizeof(sc->ndis_filter); 349124709Swpaul error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, 350124709Swpaul &sc->ndis_filter, &len); 351124709Swpaul if (error) 352198786Srpaulo device_printf(sc->ndis_dev, "set multi failed: %d\n", error); 353123474Swpaul} 354123474Swpaul 355123474Swpaulstatic int 356124821Swpaulndis_set_offload(sc) 357124821Swpaul struct ndis_softc *sc; 358124821Swpaul{ 359124821Swpaul ndis_task_offload *nto; 360124821Swpaul ndis_task_offload_hdr *ntoh; 361124821Swpaul ndis_task_tcpip_csum *nttc; 362124821Swpaul struct ifnet *ifp; 363124821Swpaul int len, error; 364124821Swpaul 365147256Sbrooks ifp = sc->ifp; 366124821Swpaul 367131750Swpaul if (!NDIS_INITIALIZED(sc)) 368198786Srpaulo return (EINVAL); 369124821Swpaul 370124821Swpaul /* See if there's anything to set. */ 371124821Swpaul 372124821Swpaul error = ndis_probe_offload(sc); 373124821Swpaul if (error) 374198786Srpaulo return (error); 375124821Swpaul 376124821Swpaul if (sc->ndis_hwassist == 0 && ifp->if_capabilities == 0) 377198786Srpaulo return (0); 378124821Swpaul 379124821Swpaul len = sizeof(ndis_task_offload_hdr) + sizeof(ndis_task_offload) + 380124821Swpaul sizeof(ndis_task_tcpip_csum); 381124821Swpaul 382124821Swpaul ntoh = malloc(len, M_TEMP, M_NOWAIT|M_ZERO); 383124821Swpaul 384124821Swpaul if (ntoh == NULL) 385198786Srpaulo return (ENOMEM); 386124821Swpaul 387124821Swpaul ntoh->ntoh_vers = NDIS_TASK_OFFLOAD_VERSION; 388124821Swpaul ntoh->ntoh_len = sizeof(ndis_task_offload_hdr); 389124821Swpaul ntoh->ntoh_offset_firsttask = sizeof(ndis_task_offload_hdr); 390124821Swpaul ntoh->ntoh_encapfmt.nef_encaphdrlen = sizeof(struct ether_header); 391124821Swpaul ntoh->ntoh_encapfmt.nef_encap = NDIS_ENCAP_IEEE802_3; 392124821Swpaul ntoh->ntoh_encapfmt.nef_flags = NDIS_ENCAPFLAG_FIXEDHDRLEN; 393124821Swpaul 394124821Swpaul nto = (ndis_task_offload *)((char *)ntoh + 395124821Swpaul ntoh->ntoh_offset_firsttask); 396124821Swpaul 397124821Swpaul nto->nto_vers = NDIS_TASK_OFFLOAD_VERSION; 398124821Swpaul nto->nto_len = sizeof(ndis_task_offload); 399124821Swpaul nto->nto_task = NDIS_TASK_TCPIP_CSUM; 400124821Swpaul nto->nto_offset_nexttask = 0; 401124821Swpaul nto->nto_taskbuflen = sizeof(ndis_task_tcpip_csum); 402124821Swpaul 403124821Swpaul nttc = (ndis_task_tcpip_csum *)nto->nto_taskbuf; 404124821Swpaul 405124821Swpaul if (ifp->if_capenable & IFCAP_TXCSUM) 406124821Swpaul nttc->nttc_v4tx = sc->ndis_v4tx; 407124821Swpaul 408124821Swpaul if (ifp->if_capenable & IFCAP_RXCSUM) 409124821Swpaul nttc->nttc_v4rx = sc->ndis_v4rx; 410124821Swpaul 411124821Swpaul error = ndis_set_info(sc, OID_TCP_TASK_OFFLOAD, ntoh, &len); 412124821Swpaul free(ntoh, M_TEMP); 413124821Swpaul 414198786Srpaulo return (error); 415124821Swpaul} 416124821Swpaul 417124821Swpaulstatic int 418124821Swpaulndis_probe_offload(sc) 419124821Swpaul struct ndis_softc *sc; 420124821Swpaul{ 421124821Swpaul ndis_task_offload *nto; 422124821Swpaul ndis_task_offload_hdr *ntoh; 423124821Swpaul ndis_task_tcpip_csum *nttc = NULL; 424124821Swpaul struct ifnet *ifp; 425124821Swpaul int len, error, dummy; 426124821Swpaul 427147256Sbrooks ifp = sc->ifp; 428124821Swpaul 429124821Swpaul len = sizeof(dummy); 430124821Swpaul error = ndis_get_info(sc, OID_TCP_TASK_OFFLOAD, &dummy, &len); 431124821Swpaul 432124821Swpaul if (error != ENOSPC) 433198786Srpaulo return (error); 434124821Swpaul 435124821Swpaul ntoh = malloc(len, M_TEMP, M_NOWAIT|M_ZERO); 436124821Swpaul 437124821Swpaul if (ntoh == NULL) 438198786Srpaulo return (ENOMEM); 439124821Swpaul 440124821Swpaul ntoh->ntoh_vers = NDIS_TASK_OFFLOAD_VERSION; 441124821Swpaul ntoh->ntoh_len = sizeof(ndis_task_offload_hdr); 442124821Swpaul ntoh->ntoh_encapfmt.nef_encaphdrlen = sizeof(struct ether_header); 443124821Swpaul ntoh->ntoh_encapfmt.nef_encap = NDIS_ENCAP_IEEE802_3; 444124821Swpaul ntoh->ntoh_encapfmt.nef_flags = NDIS_ENCAPFLAG_FIXEDHDRLEN; 445124821Swpaul 446124821Swpaul error = ndis_get_info(sc, OID_TCP_TASK_OFFLOAD, ntoh, &len); 447124821Swpaul 448124821Swpaul if (error) { 449124821Swpaul free(ntoh, M_TEMP); 450198786Srpaulo return (error); 451124821Swpaul } 452124821Swpaul 453124821Swpaul if (ntoh->ntoh_vers != NDIS_TASK_OFFLOAD_VERSION) { 454124821Swpaul free(ntoh, M_TEMP); 455198786Srpaulo return (EINVAL); 456124821Swpaul } 457124821Swpaul 458124821Swpaul nto = (ndis_task_offload *)((char *)ntoh + 459124821Swpaul ntoh->ntoh_offset_firsttask); 460124821Swpaul 461124821Swpaul while (1) { 462124821Swpaul switch (nto->nto_task) { 463124821Swpaul case NDIS_TASK_TCPIP_CSUM: 464124821Swpaul nttc = (ndis_task_tcpip_csum *)nto->nto_taskbuf; 465124821Swpaul break; 466124821Swpaul /* Don't handle these yet. */ 467124821Swpaul case NDIS_TASK_IPSEC: 468124821Swpaul case NDIS_TASK_TCP_LARGESEND: 469124821Swpaul default: 470124821Swpaul break; 471124821Swpaul } 472124821Swpaul if (nto->nto_offset_nexttask == 0) 473124821Swpaul break; 474124821Swpaul nto = (ndis_task_offload *)((char *)nto + 475124821Swpaul nto->nto_offset_nexttask); 476124821Swpaul } 477124821Swpaul 478124821Swpaul if (nttc == NULL) { 479124821Swpaul free(ntoh, M_TEMP); 480198786Srpaulo return (ENOENT); 481124821Swpaul } 482124821Swpaul 483124821Swpaul sc->ndis_v4tx = nttc->nttc_v4tx; 484124821Swpaul sc->ndis_v4rx = nttc->nttc_v4rx; 485124821Swpaul 486124821Swpaul if (nttc->nttc_v4tx & NDIS_TCPSUM_FLAGS_IP_CSUM) 487124821Swpaul sc->ndis_hwassist |= CSUM_IP; 488124821Swpaul if (nttc->nttc_v4tx & NDIS_TCPSUM_FLAGS_TCP_CSUM) 489124821Swpaul sc->ndis_hwassist |= CSUM_TCP; 490124821Swpaul if (nttc->nttc_v4tx & NDIS_TCPSUM_FLAGS_UDP_CSUM) 491124821Swpaul sc->ndis_hwassist |= CSUM_UDP; 492124821Swpaul 493124821Swpaul if (sc->ndis_hwassist) 494124821Swpaul ifp->if_capabilities |= IFCAP_TXCSUM; 495124821Swpaul 496124821Swpaul if (nttc->nttc_v4rx & NDIS_TCPSUM_FLAGS_IP_CSUM) 497124821Swpaul ifp->if_capabilities |= IFCAP_RXCSUM; 498124821Swpaul if (nttc->nttc_v4rx & NDIS_TCPSUM_FLAGS_TCP_CSUM) 499124821Swpaul ifp->if_capabilities |= IFCAP_RXCSUM; 500124821Swpaul if (nttc->nttc_v4rx & NDIS_TCPSUM_FLAGS_UDP_CSUM) 501124821Swpaul ifp->if_capabilities |= IFCAP_RXCSUM; 502124821Swpaul 503124821Swpaul free(ntoh, M_TEMP); 504198786Srpaulo return (0); 505124821Swpaul} 506124821Swpaul 507171602Sthompsastatic int 508171602Sthompsandis_nettype_chan(uint32_t type) 509171602Sthompsa{ 510171602Sthompsa switch (type) { 511171602Sthompsa case NDIS_80211_NETTYPE_11FH: return (IEEE80211_CHAN_FHSS); 512171602Sthompsa case NDIS_80211_NETTYPE_11DS: return (IEEE80211_CHAN_B); 513171602Sthompsa case NDIS_80211_NETTYPE_11OFDM5: return (IEEE80211_CHAN_A); 514171602Sthompsa case NDIS_80211_NETTYPE_11OFDM24: return (IEEE80211_CHAN_G); 515171602Sthompsa } 516171602Sthompsa DPRINTF(("unknown channel nettype %d\n", type)); 517171602Sthompsa return (IEEE80211_CHAN_B); /* Default to 11B chan */ 518171602Sthompsa} 519171602Sthompsa 520171602Sthompsastatic int 521171602Sthompsandis_nettype_mode(uint32_t type) 522171602Sthompsa{ 523171602Sthompsa switch (type) { 524171602Sthompsa case NDIS_80211_NETTYPE_11FH: return (IEEE80211_MODE_FH); 525171602Sthompsa case NDIS_80211_NETTYPE_11DS: return (IEEE80211_MODE_11B); 526171602Sthompsa case NDIS_80211_NETTYPE_11OFDM5: return (IEEE80211_MODE_11A); 527171602Sthompsa case NDIS_80211_NETTYPE_11OFDM24: return (IEEE80211_MODE_11G); 528171602Sthompsa } 529171602Sthompsa DPRINTF(("unknown mode nettype %d\n", type)); 530171602Sthompsa return (IEEE80211_MODE_AUTO); 531171602Sthompsa} 532171602Sthompsa 533123474Swpaul/* 534123474Swpaul * Attach the interface. Allocate softc structures, do ifmedia 535123474Swpaul * setup and ethernet/BPF attach. 536123474Swpaul */ 537126706Swpaulint 538123474Swpaulndis_attach(dev) 539123474Swpaul device_t dev; 540123474Swpaul{ 541123474Swpaul u_char eaddr[ETHER_ADDR_LEN]; 542126706Swpaul struct ndis_softc *sc; 543142399Swpaul driver_object *pdrv; 544141524Swpaul device_object *pdo; 545124100Swpaul struct ifnet *ifp = NULL; 546178354Ssam int error = 0, len, mode; 547178354Ssam uint8_t bands = 0; 548171390Sthompsa int i; 549123474Swpaul 550123474Swpaul sc = device_get_softc(dev); 551123474Swpaul 552179723Scokane mtx_init(&sc->ndis_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 553179723Scokane MTX_DEF); 554151451Swpaul KeInitializeSpinLock(&sc->ndis_rxlock); 555189719Sweongyo KeInitializeSpinLock(&sc->ndisusb_tasklock); 556189488Sweongyo KeInitializeSpinLock(&sc->ndisusb_xferdonelock); 557151207Swpaul InitializeListHead(&sc->ndis_shlist); 558189719Sweongyo InitializeListHead(&sc->ndisusb_tasklist); 559189488Sweongyo InitializeListHead(&sc->ndisusb_xferdonelist); 560314667Savg callout_init(&sc->ndis_stat_callout, 1); 561123474Swpaul 562123474Swpaul /* Create sysctl registry nodes */ 563123474Swpaul ndis_create_sysctls(sc); 564123474Swpaul 565142399Swpaul /* Find the PDO for this device instance. */ 566142399Swpaul 567142399Swpaul if (sc->ndis_iftype == PCIBus) 568142804Swpaul pdrv = windrv_lookup(0, "PCI Bus"); 569142399Swpaul else if (sc->ndis_iftype == PCMCIABus) 570142804Swpaul pdrv = windrv_lookup(0, "PCCARD Bus"); 571142399Swpaul else 572142804Swpaul pdrv = windrv_lookup(0, "USB Bus"); 573142399Swpaul pdo = windrv_find_pdo(pdrv, dev); 574142399Swpaul 575141524Swpaul /* 576141524Swpaul * Create a new functional device object for this 577141524Swpaul * device. This is what creates the miniport block 578141524Swpaul * for this device instance. 579141524Swpaul */ 580141524Swpaul 581145485Swpaul if (NdisAddDevice(sc->ndis_dobj, pdo) != STATUS_SUCCESS) { 582141524Swpaul device_printf(dev, "failed to create FDO!\n"); 583141524Swpaul error = ENXIO; 584141524Swpaul goto fail; 585141524Swpaul } 586123474Swpaul 587124116Swpaul /* Tell the user what version of the API the driver is using. */ 588124116Swpaul device_printf(dev, "NDIS API version: %d.%d\n", 589141524Swpaul sc->ndis_chars->nmc_version_major, 590141524Swpaul sc->ndis_chars->nmc_version_minor); 591124116Swpaul 592123474Swpaul /* Do resource conversion. */ 593142399Swpaul if (sc->ndis_iftype == PCMCIABus || sc->ndis_iftype == PCIBus) 594142399Swpaul ndis_convert_res(sc); 595142408Swpaul else 596142408Swpaul sc->ndis_block->nmb_rlist = NULL; 597123474Swpaul 598123474Swpaul /* Install our RX and TX interrupt handlers. */ 599141963Swpaul sc->ndis_block->nmb_senddone_func = ndis_txeof_wrap; 600141963Swpaul sc->ndis_block->nmb_pktind_func = ndis_rxeof_wrap; 601146230Swpaul sc->ndis_block->nmb_ethrxindicate_func = ndis_rxeof_eth_wrap; 602146230Swpaul sc->ndis_block->nmb_ethrxdone_func = ndis_rxeof_done_wrap; 603146230Swpaul sc->ndis_block->nmb_tdcond_func = ndis_rxeof_xfr_done_wrap; 604123474Swpaul 605155311Swpaul /* Override the status handler so we can detect link changes. */ 606155311Swpaul sc->ndis_block->nmb_status_func = ndis_linksts_wrap; 607155311Swpaul sc->ndis_block->nmb_statusdone_func = ndis_linksts_done_wrap; 608155311Swpaul 609155311Swpaul /* Set up work item handlers. */ 610155311Swpaul sc->ndis_tickitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); 611155311Swpaul sc->ndis_startitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); 612155311Swpaul sc->ndis_resetitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); 613155311Swpaul sc->ndis_inputitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); 614189488Sweongyo sc->ndisusb_xferdoneitem = 615189488Sweongyo IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); 616189719Sweongyo sc->ndisusb_taskitem = 617189719Sweongyo IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); 618155311Swpaul KeInitializeDpc(&sc->ndis_rxdpc, ndis_rxeof_xfr_wrap, sc->ndis_block); 619155311Swpaul 620123474Swpaul /* Call driver's init routine. */ 621123474Swpaul if (ndis_init_nic(sc)) { 622198786Srpaulo device_printf(dev, "init handler failed\n"); 623123474Swpaul error = ENXIO; 624123474Swpaul goto fail; 625123474Swpaul } 626123474Swpaul 627126833Swpaul /* 628123474Swpaul * Get station address from the driver. 629123474Swpaul */ 630123474Swpaul len = sizeof(eaddr); 631123474Swpaul ndis_get_info(sc, OID_802_3_CURRENT_ADDRESS, &eaddr, &len); 632123474Swpaul 633123474Swpaul /* 634151207Swpaul * Figure out how big to make the TX buffer pool. 635123474Swpaul */ 636123474Swpaul 637151207Swpaul len = sizeof(sc->ndis_maxpkts); 638151207Swpaul if (ndis_get_info(sc, OID_GEN_MAXIMUM_SEND_PACKETS, 639151207Swpaul &sc->ndis_maxpkts, &len)) { 640198786Srpaulo device_printf(dev, "failed to get max TX packets\n"); 641151207Swpaul error = ENXIO; 642151207Swpaul goto fail; 643123474Swpaul } 644123474Swpaul 645151451Swpaul /* 646151451Swpaul * If this is a deserialized miniport, we don't have 647151451Swpaul * to honor the OID_GEN_MAXIMUM_SEND_PACKETS result. 648151451Swpaul */ 649151451Swpaul if (!NDIS_SERIALIZED(sc->ndis_block)) 650151451Swpaul sc->ndis_maxpkts = NDIS_TXPKTS; 651151451Swpaul 652151207Swpaul /* Enforce some sanity, just in case. */ 653151207Swpaul 654151207Swpaul if (sc->ndis_maxpkts == 0) 655151207Swpaul sc->ndis_maxpkts = 10; 656151207Swpaul 657125377Swpaul sc->ndis_txarray = malloc(sizeof(ndis_packet *) * 658183587Sweongyo sc->ndis_maxpkts, M_DEVBUF, M_NOWAIT|M_ZERO); 659125377Swpaul 660141963Swpaul /* Allocate a pool of ndis_packets for TX encapsulation. */ 661141963Swpaul 662141963Swpaul NdisAllocatePacketPool(&i, &sc->ndis_txpool, 663183587Sweongyo sc->ndis_maxpkts, PROTOCOL_RESERVED_SIZE_IN_PACKET); 664141963Swpaul 665141963Swpaul if (i != NDIS_STATUS_SUCCESS) { 666141963Swpaul sc->ndis_txpool = NULL; 667141963Swpaul device_printf(dev, "failed to allocate TX packet pool"); 668141963Swpaul error = ENOMEM; 669141963Swpaul goto fail; 670141963Swpaul } 671141963Swpaul 672123474Swpaul sc->ndis_txpending = sc->ndis_maxpkts; 673123474Swpaul 674123474Swpaul sc->ndis_oidcnt = 0; 675123474Swpaul /* Get supported oid list. */ 676123474Swpaul ndis_get_supported_oids(sc, &sc->ndis_oids, &sc->ndis_oidcnt); 677123474Swpaul 678123474Swpaul /* If the NDIS module requested scatter/gather, init maps. */ 679123474Swpaul if (sc->ndis_sc) 680123474Swpaul ndis_init_dma(sc); 681123474Swpaul 682123695Swpaul /* 683125309Swpaul * See if the OID_802_11_CONFIGURATION OID is 684123695Swpaul * supported by this driver. If it is, then this an 802.11 685123695Swpaul * wireless driver, and we should set up media for wireless. 686123695Swpaul */ 687198786Srpaulo for (i = 0; i < sc->ndis_oidcnt; i++) 688125309Swpaul if (sc->ndis_oids[i] == OID_802_11_CONFIGURATION) { 689123695Swpaul sc->ndis_80211++; 690123695Swpaul break; 691123695Swpaul } 692123474Swpaul 693178354Ssam if (sc->ndis_80211) 694178354Ssam ifp = if_alloc(IFT_IEEE80211); 695178354Ssam else 696178354Ssam ifp = if_alloc(IFT_ETHER); 697178354Ssam if (ifp == NULL) { 698178354Ssam error = ENOSPC; 699178354Ssam goto fail; 700178354Ssam } 701178354Ssam sc->ifp = ifp; 702178354Ssam ifp->if_softc = sc; 703178354Ssam 704124821Swpaul /* Check for task offload support. */ 705124821Swpaul ndis_probe_offload(sc); 706124821Swpaul 707123474Swpaul if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 708123474Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 709123474Swpaul ifp->if_ioctl = ndis_ioctl; 710123474Swpaul ifp->if_start = ndis_start; 711123474Swpaul ifp->if_init = ndis_init; 712123474Swpaul ifp->if_baudrate = 10000000; 713132986Smlaier IFQ_SET_MAXLEN(&ifp->if_snd, 50); 714132986Smlaier ifp->if_snd.ifq_drv_maxlen = 25; 715132986Smlaier IFQ_SET_READY(&ifp->if_snd); 716124821Swpaul ifp->if_capenable = ifp->if_capabilities; 717124821Swpaul ifp->if_hwassist = sc->ndis_hwassist; 718123474Swpaul 719123695Swpaul /* Do media setup */ 720123695Swpaul if (sc->ndis_80211) { 721178354Ssam struct ieee80211com *ic = ifp->if_l2com; 722130051Swpaul ndis_80211_rates_ex rates; 723124409Swpaul struct ndis_80211_nettype_list *ntl; 724123695Swpaul uint32_t arg; 725123695Swpaul int r; 726123695Swpaul 727314667Savg callout_init(&sc->ndis_scan_callout, 1); 728171390Sthompsa 729178704Sthompsa ifp->if_ioctl = ndis_ioctl_80211; 730138572Ssam ic->ic_ifp = ifp; 731178354Ssam ic->ic_opmode = IEEE80211_M_STA; 732123695Swpaul ic->ic_phytype = IEEE80211_T_DS; 733190850Ssam ic->ic_caps = IEEE80211_C_8023ENCAP | 734190850Ssam IEEE80211_C_STA | IEEE80211_C_IBSS; 735167468Ssam setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); 736124409Swpaul len = 0; 737124409Swpaul r = ndis_get_info(sc, OID_802_11_NETWORK_TYPES_SUPPORTED, 738124409Swpaul NULL, &len); 739124409Swpaul if (r != ENOSPC) 740124409Swpaul goto nonettypes; 741151207Swpaul ntl = malloc(len, M_DEVBUF, M_NOWAIT|M_ZERO); 742124409Swpaul r = ndis_get_info(sc, OID_802_11_NETWORK_TYPES_SUPPORTED, 743124409Swpaul ntl, &len); 744124409Swpaul if (r != 0) { 745124409Swpaul free(ntl, M_DEVBUF); 746124409Swpaul goto nonettypes; 747124409Swpaul } 748124409Swpaul 749124409Swpaul for (i = 0; i < ntl->ntl_items; i++) { 750171602Sthompsa mode = ndis_nettype_mode(ntl->ntl_type[i]); 751171602Sthompsa if (mode) { 752171602Sthompsa setbit(ic->ic_modecaps, mode); 753171602Sthompsa setbit(&bands, mode); 754171602Sthompsa } else 755171602Sthompsa device_printf(dev, "Unknown nettype %d\n", 756171602Sthompsa ntl->ntl_type[i]); 757124409Swpaul } 758124409Swpaul free(ntl, M_DEVBUF); 759124409Swpaulnonettypes: 760171602Sthompsa /* Default to 11b channels if the card did not supply any */ 761171602Sthompsa if (bands == 0) { 762171602Sthompsa setbit(ic->ic_modecaps, IEEE80211_MODE_11B); 763171602Sthompsa setbit(&bands, IEEE80211_MODE_11B); 764171602Sthompsa } 765123695Swpaul len = sizeof(rates); 766123695Swpaul bzero((char *)&rates, len); 767123695Swpaul r = ndis_get_info(sc, OID_802_11_SUPPORTED_RATES, 768123695Swpaul (void *)rates, &len); 769123695Swpaul if (r) 770198786Srpaulo device_printf(dev, "get rates failed: 0x%x\n", r); 771123695Swpaul /* 772124409Swpaul * Since the supported rates only up to 8 can be supported, 773124409Swpaul * if this is not 802.11b we're just going to be faking it 774124409Swpaul * all up to heck. 775123695Swpaul */ 776127887Swpaul 777127887Swpaul#define TESTSETRATE(x, y) \ 778127887Swpaul do { \ 779127887Swpaul int i; \ 780127887Swpaul for (i = 0; i < ic->ic_sup_rates[x].rs_nrates; i++) { \ 781127887Swpaul if (ic->ic_sup_rates[x].rs_rates[i] == (y)) \ 782127887Swpaul break; \ 783127887Swpaul } \ 784127887Swpaul if (i == ic->ic_sup_rates[x].rs_nrates) { \ 785127887Swpaul ic->ic_sup_rates[x].rs_rates[i] = (y); \ 786127887Swpaul ic->ic_sup_rates[x].rs_nrates++; \ 787127887Swpaul } \ 788127887Swpaul } while (0) 789127887Swpaul 790123695Swpaul#define SETRATE(x, y) \ 791124409Swpaul ic->ic_sup_rates[x].rs_rates[ic->ic_sup_rates[x].rs_nrates] = (y) 792123695Swpaul#define INCRATE(x) \ 793124409Swpaul ic->ic_sup_rates[x].rs_nrates++ 794123695Swpaul 795124409Swpaul ic->ic_curmode = IEEE80211_MODE_AUTO; 796167468Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11A)) 797124409Swpaul ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates = 0; 798167468Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11B)) 799123695Swpaul ic->ic_sup_rates[IEEE80211_MODE_11B].rs_nrates = 0; 800167468Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11G)) 801123695Swpaul ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates = 0; 802124409Swpaul for (i = 0; i < len; i++) { 803124409Swpaul switch (rates[i] & IEEE80211_RATE_VAL) { 804124409Swpaul case 2: 805124409Swpaul case 4: 806124409Swpaul case 11: 807124409Swpaul case 10: 808124409Swpaul case 22: 809167468Ssam if (isclr(ic->ic_modecaps, IEEE80211_MODE_11B)) { 810124409Swpaul /* Lazy-init 802.11b. */ 811167468Ssam setbit(ic->ic_modecaps, 812167468Ssam IEEE80211_MODE_11B); 813124409Swpaul ic->ic_sup_rates[IEEE80211_MODE_11B]. 814124409Swpaul rs_nrates = 0; 815124409Swpaul } 816124409Swpaul SETRATE(IEEE80211_MODE_11B, rates[i]); 817124409Swpaul INCRATE(IEEE80211_MODE_11B); 818124409Swpaul break; 819124409Swpaul default: 820167468Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11A)) { 821124409Swpaul SETRATE(IEEE80211_MODE_11A, rates[i]); 822124409Swpaul INCRATE(IEEE80211_MODE_11A); 823124409Swpaul } 824167468Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11G)) { 825123695Swpaul SETRATE(IEEE80211_MODE_11G, rates[i]); 826123695Swpaul INCRATE(IEEE80211_MODE_11G); 827123695Swpaul } 828124409Swpaul break; 829123695Swpaul } 830124409Swpaul } 831123695Swpaul 832124409Swpaul /* 833124409Swpaul * If the hardware supports 802.11g, it most 834124409Swpaul * likely supports 802.11b and all of the 835124409Swpaul * 802.11b and 802.11g speeds, so maybe we can 836124409Swpaul * just cheat here. Just how in the heck do 837124409Swpaul * we detect turbo modes, though? 838124409Swpaul */ 839167468Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11B)) { 840130051Swpaul TESTSETRATE(IEEE80211_MODE_11B, 841130051Swpaul IEEE80211_RATE_BASIC|2); 842130051Swpaul TESTSETRATE(IEEE80211_MODE_11B, 843130051Swpaul IEEE80211_RATE_BASIC|4); 844130051Swpaul TESTSETRATE(IEEE80211_MODE_11B, 845130051Swpaul IEEE80211_RATE_BASIC|11); 846130051Swpaul TESTSETRATE(IEEE80211_MODE_11B, 847130051Swpaul IEEE80211_RATE_BASIC|22); 848129834Swpaul } 849167468Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11G)) { 850186919Sweongyo TESTSETRATE(IEEE80211_MODE_11G, 48); 851127887Swpaul TESTSETRATE(IEEE80211_MODE_11G, 72); 852127887Swpaul TESTSETRATE(IEEE80211_MODE_11G, 96); 853127887Swpaul TESTSETRATE(IEEE80211_MODE_11G, 108); 854123695Swpaul } 855167468Ssam if (isset(ic->ic_modecaps, IEEE80211_MODE_11A)) { 856186919Sweongyo TESTSETRATE(IEEE80211_MODE_11A, 48); 857127887Swpaul TESTSETRATE(IEEE80211_MODE_11A, 72); 858127887Swpaul TESTSETRATE(IEEE80211_MODE_11A, 96); 859127887Swpaul TESTSETRATE(IEEE80211_MODE_11A, 108); 860124409Swpaul } 861123695Swpaul#undef SETRATE 862123695Swpaul#undef INCRATE 863178354Ssam ieee80211_init_channels(ic, NULL, &bands); 864124409Swpaul 865151207Swpaul /* 866151207Swpaul * To test for WPA support, we need to see if we can 867151207Swpaul * set AUTHENTICATION_MODE to WPA and read it back 868151207Swpaul * successfully. 869151207Swpaul */ 870123695Swpaul i = sizeof(arg); 871151207Swpaul arg = NDIS_80211_AUTHMODE_WPA; 872151207Swpaul r = ndis_set_info(sc, 873151207Swpaul OID_802_11_AUTHENTICATION_MODE, &arg, &i); 874151207Swpaul if (r == 0) { 875151207Swpaul r = ndis_get_info(sc, 876151207Swpaul OID_802_11_AUTHENTICATION_MODE, &arg, &i); 877151207Swpaul if (r == 0 && arg == NDIS_80211_AUTHMODE_WPA) 878151207Swpaul ic->ic_caps |= IEEE80211_C_WPA; 879151207Swpaul } 880151207Swpaul 881151207Swpaul /* 882151207Swpaul * To test for supported ciphers, we set each 883151207Swpaul * available encryption type in descending order. 884151207Swpaul * If ENC3 works, then we have WEP, TKIP and AES. 885151207Swpaul * If only ENC2 works, then we have WEP and TKIP. 886151207Swpaul * If only ENC1 works, then we have just WEP. 887151207Swpaul */ 888151207Swpaul i = sizeof(arg); 889151207Swpaul arg = NDIS_80211_WEPSTAT_ENC3ENABLED; 890151207Swpaul r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); 891151207Swpaul if (r == 0) { 892178354Ssam ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP 893178354Ssam | IEEE80211_CRYPTO_TKIP 894178354Ssam | IEEE80211_CRYPTO_AES_CCM; 895151207Swpaul goto got_crypto; 896151207Swpaul } 897151207Swpaul arg = NDIS_80211_WEPSTAT_ENC2ENABLED; 898151207Swpaul r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); 899151207Swpaul if (r == 0) { 900178354Ssam ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP 901178354Ssam | IEEE80211_CRYPTO_TKIP; 902151207Swpaul goto got_crypto; 903151207Swpaul } 904151207Swpaul arg = NDIS_80211_WEPSTAT_ENC1ENABLED; 905151207Swpaul r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); 906151207Swpaul if (r == 0) 907178354Ssam ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP; 908151207Swpaulgot_crypto: 909124409Swpaul i = sizeof(arg); 910124409Swpaul r = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &i); 911124409Swpaul if (r == 0) 912124409Swpaul ic->ic_caps |= IEEE80211_C_PMGT; 913189550Ssam 914189550Ssam r = ndis_get_info(sc, OID_802_11_TX_POWER_LEVEL, &arg, &i); 915189550Ssam if (r == 0) 916189550Ssam ic->ic_caps |= IEEE80211_C_TXPMGT; 917189550Ssam 918190526Ssam ieee80211_ifattach(ic, eaddr); 919178354Ssam ic->ic_raw_xmit = ndis_raw_xmit; 920171390Sthompsa ic->ic_scan_start = ndis_scan_start; 921171390Sthompsa ic->ic_scan_end = ndis_scan_end; 922171390Sthompsa ic->ic_set_channel = ndis_set_channel; 923171390Sthompsa ic->ic_scan_curchan = ndis_scan_curchan; 924171390Sthompsa ic->ic_scan_mindwell = ndis_scan_mindwell; 925170530Ssam ic->ic_bsschan = IEEE80211_CHAN_ANYC; 926178354Ssam //ic->ic_bss->ni_chan = ic->ic_bsschan; 927178354Ssam ic->ic_vap_create = ndis_vap_create; 928178354Ssam ic->ic_vap_delete = ndis_vap_delete; 929185485Ssam ic->ic_update_mcast = ndis_update_mcast; 930185485Ssam ic->ic_update_promisc = ndis_update_promisc; 931178354Ssam 932194706Scokane if (bootverbose) 933194706Scokane ieee80211_announce(ic); 934194706Scokane 935123695Swpaul } else { 936123695Swpaul ifmedia_init(&sc->ifmedia, IFM_IMASK, ndis_ifmedia_upd, 937123695Swpaul ndis_ifmedia_sts); 938123695Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 939123695Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 940123695Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); 941123695Swpaul ifmedia_add(&sc->ifmedia, 942123695Swpaul IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); 943123695Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); 944123695Swpaul ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_AUTO); 945124409Swpaul ether_ifattach(ifp, eaddr); 946123695Swpaul } 947123695Swpaul 948123474Swpaulfail: 949186507Sweongyo if (error) { 950123474Swpaul ndis_detach(dev); 951186507Sweongyo return (error); 952186507Sweongyo } 953123474Swpaul 954186507Sweongyo if (sc->ndis_iftype == PNPBus && ndisusb_halt == 0) 955186507Sweongyo return (error); 956186507Sweongyo 957189488Sweongyo DPRINTF(("attach done.\n")); 958186507Sweongyo /* We're done talking to the NIC for now; halt it. */ 959186507Sweongyo ndis_halt_nic(sc); 960189488Sweongyo DPRINTF(("halting done.\n")); 961186507Sweongyo 962198786Srpaulo return (error); 963123474Swpaul} 964123474Swpaul 965178354Ssamstatic struct ieee80211vap * 966228621Sbschmidtndis_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 967228621Sbschmidt enum ieee80211_opmode opmode, int flags, 968228621Sbschmidt const uint8_t bssid[IEEE80211_ADDR_LEN], 969228621Sbschmidt const uint8_t mac[IEEE80211_ADDR_LEN]) 970178354Ssam{ 971178354Ssam struct ndis_vap *nvp; 972178354Ssam struct ieee80211vap *vap; 973178354Ssam 974178354Ssam if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 975178354Ssam return NULL; 976178354Ssam nvp = (struct ndis_vap *) malloc(sizeof(struct ndis_vap), 977178354Ssam M_80211_VAP, M_NOWAIT | M_ZERO); 978178354Ssam if (nvp == NULL) 979178354Ssam return NULL; 980178354Ssam vap = &nvp->vap; 981178354Ssam ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); 982178354Ssam /* override with driver methods */ 983178354Ssam nvp->newstate = vap->iv_newstate; 984178354Ssam vap->iv_newstate = ndis_newstate; 985178354Ssam 986178354Ssam /* complete setup */ 987201620Srpaulo ieee80211_vap_attach(vap, ieee80211_media_change, ndis_media_status); 988178354Ssam ic->ic_opmode = opmode; 989178354Ssam /* install key handing routines */ 990178354Ssam vap->iv_key_set = ndis_add_key; 991178354Ssam vap->iv_key_delete = ndis_del_key; 992178354Ssam return vap; 993178354Ssam} 994178354Ssam 995178354Ssamstatic void 996178354Ssamndis_vap_delete(struct ieee80211vap *vap) 997178354Ssam{ 998178354Ssam struct ndis_vap *nvp = NDIS_VAP(vap); 999197403Scokane struct ieee80211com *ic = vap->iv_ic; 1000197403Scokane struct ifnet *ifp = ic->ic_ifp; 1001197403Scokane struct ndis_softc *sc = ifp->if_softc; 1002178354Ssam 1003197403Scokane ndis_stop(sc); 1004197403Scokane callout_drain(&sc->ndis_scan_callout); 1005178354Ssam ieee80211_vap_detach(vap); 1006178354Ssam free(nvp, M_80211_VAP); 1007178354Ssam} 1008178354Ssam 1009123474Swpaul/* 1010123474Swpaul * Shutdown hardware and free up resources. This can be called any 1011123474Swpaul * time after the mutex has been initialized. It is called in both 1012123474Swpaul * the error case in attach and the normal detach case so it needs 1013123474Swpaul * to be careful about only freeing resources that have actually been 1014123474Swpaul * allocated. 1015123474Swpaul */ 1016126706Swpaulint 1017123474Swpaulndis_detach(dev) 1018123474Swpaul device_t dev; 1019123474Swpaul{ 1020123757Swpaul struct ndis_softc *sc; 1021123474Swpaul struct ifnet *ifp; 1022141524Swpaul driver_object *drv; 1023123474Swpaul 1024123474Swpaul sc = device_get_softc(dev); 1025123474Swpaul NDIS_LOCK(sc); 1026147256Sbrooks ifp = sc->ifp; 1027179424Sweongyo if (ifp != NULL) 1028179424Sweongyo ifp->if_flags &= ~IFF_UP; 1029123474Swpaul 1030123474Swpaul if (device_is_attached(dev)) { 1031123474Swpaul NDIS_UNLOCK(sc); 1032123474Swpaul ndis_stop(sc); 1033179424Sweongyo if (ifp != NULL) { 1034179424Sweongyo if (sc->ndis_80211) 1035179424Sweongyo ieee80211_ifdetach(ifp->if_l2com); 1036179424Sweongyo else 1037179424Sweongyo ether_ifdetach(ifp); 1038179424Sweongyo } 1039123695Swpaul } else 1040123695Swpaul NDIS_UNLOCK(sc); 1041123474Swpaul 1042151207Swpaul if (sc->ndis_tickitem != NULL) 1043151207Swpaul IoFreeWorkItem(sc->ndis_tickitem); 1044151207Swpaul if (sc->ndis_startitem != NULL) 1045151207Swpaul IoFreeWorkItem(sc->ndis_startitem); 1046151207Swpaul if (sc->ndis_resetitem != NULL) 1047151207Swpaul IoFreeWorkItem(sc->ndis_resetitem); 1048151451Swpaul if (sc->ndis_inputitem != NULL) 1049151451Swpaul IoFreeWorkItem(sc->ndis_inputitem); 1050189488Sweongyo if (sc->ndisusb_xferdoneitem != NULL) 1051189488Sweongyo IoFreeWorkItem(sc->ndisusb_xferdoneitem); 1052189719Sweongyo if (sc->ndisusb_taskitem != NULL) 1053189719Sweongyo IoFreeWorkItem(sc->ndisusb_taskitem); 1054151207Swpaul 1055123474Swpaul bus_generic_detach(dev); 1056171602Sthompsa ndis_unload_driver(sc); 1057123474Swpaul 1058123474Swpaul if (sc->ndis_irq) 1059123474Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ndis_irq); 1060123474Swpaul if (sc->ndis_res_io) 1061123474Swpaul bus_release_resource(dev, SYS_RES_IOPORT, 1062123474Swpaul sc->ndis_io_rid, sc->ndis_res_io); 1063123474Swpaul if (sc->ndis_res_mem) 1064123474Swpaul bus_release_resource(dev, SYS_RES_MEMORY, 1065123474Swpaul sc->ndis_mem_rid, sc->ndis_res_mem); 1066123976Swpaul if (sc->ndis_res_altmem) 1067123976Swpaul bus_release_resource(dev, SYS_RES_MEMORY, 1068123976Swpaul sc->ndis_altmem_rid, sc->ndis_res_altmem); 1069123474Swpaul 1070150306Simp if (ifp != NULL) 1071150306Simp if_free(ifp); 1072150306Simp 1073123474Swpaul if (sc->ndis_sc) 1074123474Swpaul ndis_destroy_dma(sc); 1075123474Swpaul 1076133080Swpaul if (sc->ndis_txarray) 1077133080Swpaul free(sc->ndis_txarray, M_DEVBUF); 1078133080Swpaul 1079133080Swpaul if (!sc->ndis_80211) 1080133080Swpaul ifmedia_removeall(&sc->ifmedia); 1081133080Swpaul 1082141963Swpaul if (sc->ndis_txpool != NULL) 1083141963Swpaul NdisFreePacketPool(sc->ndis_txpool); 1084141963Swpaul 1085141524Swpaul /* Destroy the PDO for this device. */ 1086141524Swpaul 1087142399Swpaul if (sc->ndis_iftype == PCIBus) 1088142804Swpaul drv = windrv_lookup(0, "PCI Bus"); 1089142399Swpaul else if (sc->ndis_iftype == PCMCIABus) 1090142804Swpaul drv = windrv_lookup(0, "PCCARD Bus"); 1091142399Swpaul else 1092142804Swpaul drv = windrv_lookup(0, "USB Bus"); 1093141524Swpaul if (drv == NULL) 1094141524Swpaul panic("couldn't find driver object"); 1095141524Swpaul windrv_destroy_pdo(drv, dev); 1096141524Swpaul 1097126706Swpaul if (sc->ndis_iftype == PCIBus) 1098126706Swpaul bus_dma_tag_destroy(sc->ndis_parent_tag); 1099123474Swpaul 1100198786Srpaulo return (0); 1101123474Swpaul} 1102123474Swpaul 1103126706Swpaulint 1104124822Swpaulndis_suspend(dev) 1105124822Swpaul device_t dev; 1106124822Swpaul{ 1107124822Swpaul struct ndis_softc *sc; 1108124822Swpaul struct ifnet *ifp; 1109124822Swpaul 1110124822Swpaul sc = device_get_softc(dev); 1111147256Sbrooks ifp = sc->ifp; 1112124822Swpaul 1113125309Swpaul#ifdef notdef 1114131750Swpaul if (NDIS_INITIALIZED(sc)) 1115124822Swpaul ndis_stop(sc); 1116125309Swpaul#endif 1117124822Swpaul 1118198786Srpaulo return (0); 1119124822Swpaul} 1120124822Swpaul 1121126706Swpaulint 1122124822Swpaulndis_resume(dev) 1123124822Swpaul device_t dev; 1124124822Swpaul{ 1125124822Swpaul struct ndis_softc *sc; 1126124822Swpaul struct ifnet *ifp; 1127124822Swpaul 1128124822Swpaul sc = device_get_softc(dev); 1129147256Sbrooks ifp = sc->ifp; 1130124822Swpaul 1131131750Swpaul if (NDIS_INITIALIZED(sc)) 1132124822Swpaul ndis_init(sc); 1133124822Swpaul 1134198786Srpaulo return (0); 1135124822Swpaul} 1136124822Swpaul 1137123474Swpaul/* 1138146230Swpaul * The following bunch of routines are here to support drivers that 1139146230Swpaul * use the NdisMEthIndicateReceive()/MiniportTransferData() mechanism. 1140151451Swpaul * The NdisMEthIndicateReceive() handler runs at DISPATCH_LEVEL for 1141151451Swpaul * serialized miniports, or IRQL <= DISPATCH_LEVEL for deserialized 1142151451Swpaul * miniports. 1143146230Swpaul */ 1144146230Swpaulstatic void 1145146230Swpaulndis_rxeof_eth(adapter, ctx, addr, hdr, hdrlen, lookahead, lookaheadlen, pktlen) 1146146230Swpaul ndis_handle adapter; 1147146230Swpaul ndis_handle ctx; 1148146230Swpaul char *addr; 1149146230Swpaul void *hdr; 1150146230Swpaul uint32_t hdrlen; 1151146230Swpaul void *lookahead; 1152146230Swpaul uint32_t lookaheadlen; 1153146230Swpaul uint32_t pktlen; 1154146230Swpaul{ 1155146230Swpaul ndis_miniport_block *block; 1156169800Smjacob uint8_t irql = 0; 1157146230Swpaul uint32_t status; 1158146230Swpaul ndis_buffer *b; 1159146230Swpaul ndis_packet *p; 1160146230Swpaul struct mbuf *m; 1161146230Swpaul ndis_ethpriv *priv; 1162146230Swpaul 1163146230Swpaul block = adapter; 1164146230Swpaul 1165243857Sglebius m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 1166217118Sbschmidt if (m == NULL) 1167146230Swpaul return; 1168146230Swpaul 1169146230Swpaul /* Save the data provided to us so far. */ 1170146230Swpaul 1171146230Swpaul m->m_len = lookaheadlen + hdrlen; 1172146230Swpaul m->m_pkthdr.len = pktlen + hdrlen; 1173146230Swpaul m->m_next = NULL; 1174146230Swpaul m_copyback(m, 0, hdrlen, hdr); 1175146230Swpaul m_copyback(m, hdrlen, lookaheadlen, lookahead); 1176146230Swpaul 1177146230Swpaul /* Now create a fake NDIS_PACKET to hold the data */ 1178146230Swpaul 1179146230Swpaul NdisAllocatePacket(&status, &p, block->nmb_rxpool); 1180146230Swpaul 1181146230Swpaul if (status != NDIS_STATUS_SUCCESS) { 1182146230Swpaul m_freem(m); 1183146230Swpaul return; 1184146230Swpaul } 1185146230Swpaul 1186146230Swpaul p->np_m0 = m; 1187146230Swpaul 1188146230Swpaul b = IoAllocateMdl(m->m_data, m->m_pkthdr.len, FALSE, FALSE, NULL); 1189146230Swpaul 1190146230Swpaul if (b == NULL) { 1191146230Swpaul NdisFreePacket(p); 1192146230Swpaul m_freem(m); 1193146230Swpaul return; 1194146230Swpaul } 1195146230Swpaul 1196146230Swpaul p->np_private.npp_head = p->np_private.npp_tail = b; 1197146230Swpaul p->np_private.npp_totlen = m->m_pkthdr.len; 1198146230Swpaul 1199146230Swpaul /* Save the packet RX context somewhere. */ 1200146230Swpaul priv = (ndis_ethpriv *)&p->np_protocolreserved; 1201146230Swpaul priv->nep_ctx = ctx; 1202146230Swpaul 1203153480Swpaul if (!NDIS_SERIALIZED(block)) 1204153480Swpaul KeAcquireSpinLock(&block->nmb_lock, &irql); 1205146230Swpaul 1206151451Swpaul InsertTailList((&block->nmb_packetlist), (&p->np_list)); 1207146230Swpaul 1208153480Swpaul if (!NDIS_SERIALIZED(block)) 1209153480Swpaul KeReleaseSpinLock(&block->nmb_lock, irql); 1210146230Swpaul} 1211146230Swpaul 1212151451Swpaul/* 1213151451Swpaul * NdisMEthIndicateReceiveComplete() handler, runs at DISPATCH_LEVEL 1214151451Swpaul * for serialized miniports, or IRQL <= DISPATCH_LEVEL for deserialized 1215151451Swpaul * miniports. 1216151451Swpaul */ 1217146230Swpaulstatic void 1218146230Swpaulndis_rxeof_done(adapter) 1219146230Swpaul ndis_handle adapter; 1220146230Swpaul{ 1221146230Swpaul struct ndis_softc *sc; 1222146230Swpaul ndis_miniport_block *block; 1223146230Swpaul 1224146230Swpaul block = adapter; 1225146230Swpaul 1226146230Swpaul /* Schedule transfer/RX of queued packets. */ 1227146230Swpaul 1228146230Swpaul sc = device_get_softc(block->nmb_physdeviceobj->do_devext); 1229146230Swpaul 1230146230Swpaul KeInsertQueueDpc(&sc->ndis_rxdpc, NULL, NULL); 1231146230Swpaul} 1232146230Swpaul 1233146230Swpaul/* 1234151451Swpaul * MiniportTransferData() handler, runs at DISPATCH_LEVEL. 1235146230Swpaul */ 1236146230Swpaulstatic void 1237146230Swpaulndis_rxeof_xfr(dpc, adapter, sysarg1, sysarg2) 1238146230Swpaul kdpc *dpc; 1239146230Swpaul ndis_handle adapter; 1240146230Swpaul void *sysarg1; 1241146230Swpaul void *sysarg2; 1242146230Swpaul{ 1243146230Swpaul ndis_miniport_block *block; 1244146230Swpaul struct ndis_softc *sc; 1245146230Swpaul ndis_packet *p; 1246146230Swpaul list_entry *l; 1247146230Swpaul uint32_t status; 1248146230Swpaul ndis_ethpriv *priv; 1249146230Swpaul struct ifnet *ifp; 1250146230Swpaul struct mbuf *m; 1251146230Swpaul 1252146230Swpaul block = adapter; 1253146230Swpaul sc = device_get_softc(block->nmb_physdeviceobj->do_devext); 1254147256Sbrooks ifp = sc->ifp; 1255146230Swpaul 1256146230Swpaul KeAcquireSpinLockAtDpcLevel(&block->nmb_lock); 1257146230Swpaul 1258146230Swpaul l = block->nmb_packetlist.nle_flink; 1259151207Swpaul while(!IsListEmpty(&block->nmb_packetlist)) { 1260151207Swpaul l = RemoveHeadList((&block->nmb_packetlist)); 1261151451Swpaul p = CONTAINING_RECORD(l, ndis_packet, np_list); 1262151451Swpaul InitializeListHead((&p->np_list)); 1263146230Swpaul 1264146230Swpaul priv = (ndis_ethpriv *)&p->np_protocolreserved; 1265146230Swpaul m = p->np_m0; 1266146230Swpaul p->np_softc = sc; 1267146230Swpaul p->np_m0 = NULL; 1268146230Swpaul 1269146230Swpaul KeReleaseSpinLockFromDpcLevel(&block->nmb_lock); 1270146230Swpaul 1271146230Swpaul status = MSCALL6(sc->ndis_chars->nmc_transferdata_func, 1272146230Swpaul p, &p->np_private.npp_totlen, block, priv->nep_ctx, 1273146230Swpaul m->m_len, m->m_pkthdr.len - m->m_len); 1274146230Swpaul 1275146230Swpaul KeAcquireSpinLockAtDpcLevel(&block->nmb_lock); 1276146230Swpaul 1277146230Swpaul /* 1278146230Swpaul * If status is NDIS_STATUS_PENDING, do nothing and 1279146230Swpaul * wait for a callback to the ndis_rxeof_xfr_done() 1280146230Swpaul * handler. 1281146230Swpaul */ 1282146230Swpaul 1283146230Swpaul m->m_len = m->m_pkthdr.len; 1284146230Swpaul m->m_pkthdr.rcvif = ifp; 1285146230Swpaul 1286146230Swpaul if (status == NDIS_STATUS_SUCCESS) { 1287146230Swpaul IoFreeMdl(p->np_private.npp_head); 1288146230Swpaul NdisFreePacket(p); 1289151451Swpaul KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); 1290151451Swpaul _IF_ENQUEUE(&sc->ndis_rxqueue, m); 1291151451Swpaul KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); 1292151451Swpaul IoQueueWorkItem(sc->ndis_inputitem, 1293151451Swpaul (io_workitem_func)ndis_inputtask_wrap, 1294151451Swpaul WORKQUEUE_CRITICAL, ifp); 1295146230Swpaul } 1296146230Swpaul 1297146230Swpaul if (status == NDIS_STATUS_FAILURE) 1298146230Swpaul m_freem(m); 1299146230Swpaul 1300146230Swpaul /* Advance to next packet */ 1301146230Swpaul l = block->nmb_packetlist.nle_flink; 1302146230Swpaul } 1303146230Swpaul 1304146230Swpaul KeReleaseSpinLockFromDpcLevel(&block->nmb_lock); 1305146230Swpaul} 1306146230Swpaul 1307151451Swpaul/* 1308151451Swpaul * NdisMTransferDataComplete() handler, runs at DISPATCH_LEVEL. 1309151451Swpaul */ 1310146230Swpaulstatic void 1311146230Swpaulndis_rxeof_xfr_done(adapter, packet, status, len) 1312146230Swpaul ndis_handle adapter; 1313146230Swpaul ndis_packet *packet; 1314146230Swpaul uint32_t status; 1315146230Swpaul uint32_t len; 1316146230Swpaul{ 1317146230Swpaul ndis_miniport_block *block; 1318146230Swpaul struct ndis_softc *sc; 1319146230Swpaul struct ifnet *ifp; 1320146230Swpaul struct mbuf *m; 1321146230Swpaul 1322146230Swpaul block = adapter; 1323146230Swpaul sc = device_get_softc(block->nmb_physdeviceobj->do_devext); 1324147256Sbrooks ifp = sc->ifp; 1325146230Swpaul 1326146230Swpaul m = packet->np_m0; 1327146230Swpaul IoFreeMdl(packet->np_private.npp_head); 1328146230Swpaul NdisFreePacket(packet); 1329146230Swpaul 1330146230Swpaul if (status != NDIS_STATUS_SUCCESS) { 1331146230Swpaul m_freem(m); 1332146230Swpaul return; 1333146230Swpaul } 1334146230Swpaul 1335146230Swpaul m->m_len = m->m_pkthdr.len; 1336146230Swpaul m->m_pkthdr.rcvif = ifp; 1337151451Swpaul KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); 1338151451Swpaul _IF_ENQUEUE(&sc->ndis_rxqueue, m); 1339151451Swpaul KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); 1340151451Swpaul IoQueueWorkItem(sc->ndis_inputitem, 1341151451Swpaul (io_workitem_func)ndis_inputtask_wrap, 1342151451Swpaul WORKQUEUE_CRITICAL, ifp); 1343146230Swpaul} 1344146230Swpaul/* 1345123474Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 1346123474Swpaul * the higher level protocols. 1347123858Swpaul * 1348123858Swpaul * When handling received NDIS packets, the 'status' field in the 1349123858Swpaul * out-of-band portion of the ndis_packet has special meaning. In the 1350123858Swpaul * most common case, the underlying NDIS driver will set this field 1351123858Swpaul * to NDIS_STATUS_SUCCESS, which indicates that it's ok for us to 1352123858Swpaul * take posession of it. We then change the status field to 1353123858Swpaul * NDIS_STATUS_PENDING to tell the driver that we now own the packet, 1354123858Swpaul * and that we will return it at some point in the future via the 1355123858Swpaul * return packet handler. 1356123858Swpaul * 1357123858Swpaul * If the driver hands us a packet with a status of NDIS_STATUS_RESOURCES, 1358123858Swpaul * this means the driver is running out of packet/buffer resources and 1359123858Swpaul * wants to maintain ownership of the packet. In this case, we have to 1360123858Swpaul * copy the packet data into local storage and let the driver keep the 1361123858Swpaul * packet. 1362126833Swpaul */ 1363144888Swpaulstatic void 1364128510Swpaulndis_rxeof(adapter, packets, pktcnt) 1365126833Swpaul ndis_handle adapter; 1366126833Swpaul ndis_packet **packets; 1367126833Swpaul uint32_t pktcnt; 1368126833Swpaul{ 1369126833Swpaul struct ndis_softc *sc; 1370126833Swpaul ndis_miniport_block *block; 1371126833Swpaul ndis_packet *p; 1372126833Swpaul uint32_t s; 1373126833Swpaul ndis_tcpip_csum *csum; 1374126833Swpaul struct ifnet *ifp; 1375126833Swpaul struct mbuf *m0, *m; 1376126833Swpaul int i; 1377126833Swpaul 1378126833Swpaul block = (ndis_miniport_block *)adapter; 1379141524Swpaul sc = device_get_softc(block->nmb_physdeviceobj->do_devext); 1380147256Sbrooks ifp = sc->ifp; 1381126833Swpaul 1382155311Swpaul /* 1383155311Swpaul * There's a slim chance the driver may indicate some packets 1384155311Swpaul * before we're completely ready to handle them. If we detect this, 1385155311Swpaul * we need to return them to the miniport and ignore them. 1386155311Swpaul */ 1387155311Swpaul if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1388155311Swpaul for (i = 0; i < pktcnt; i++) { 1389155311Swpaul p = packets[i]; 1390155311Swpaul if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) { 1391155311Swpaul p->np_refcnt++; 1392254842Sandre (void)ndis_return_packet(NULL ,p, block); 1393155311Swpaul } 1394155311Swpaul } 1395155311Swpaul return; 1396155311Swpaul } 1397155311Swpaul 1398126833Swpaul for (i = 0; i < pktcnt; i++) { 1399126833Swpaul p = packets[i]; 1400126833Swpaul /* Stash the softc here so ptom can use it. */ 1401126833Swpaul p->np_softc = sc; 1402126833Swpaul if (ndis_ptom(&m0, p)) { 1403198786Srpaulo device_printf(sc->ndis_dev, "ptom failed\n"); 1404126833Swpaul if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) 1405254842Sandre (void)ndis_return_packet(NULL, p, block); 1406126833Swpaul } else { 1407151207Swpaul#ifdef notdef 1408126833Swpaul if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) { 1409243857Sglebius m = m_dup(m0, M_NOWAIT); 1410126833Swpaul /* 1411126833Swpaul * NOTE: we want to destroy the mbuf here, but 1412126833Swpaul * we don't actually want to return it to the 1413126833Swpaul * driver via the return packet handler. By 1414126833Swpaul * bumping np_refcnt, we can prevent the 1415126833Swpaul * ndis_return_packet() routine from actually 1416126833Swpaul * doing anything. 1417126833Swpaul */ 1418126833Swpaul p->np_refcnt++; 1419126833Swpaul m_freem(m0); 1420126833Swpaul if (m == NULL) 1421126833Swpaul ifp->if_ierrors++; 1422126833Swpaul else 1423126833Swpaul m0 = m; 1424126833Swpaul } else 1425126833Swpaul p->np_oob.npo_status = NDIS_STATUS_PENDING; 1426151207Swpaul#endif 1427243857Sglebius m = m_dup(m0, M_NOWAIT); 1428151207Swpaul if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) 1429151207Swpaul p->np_refcnt++; 1430151207Swpaul else 1431151207Swpaul p->np_oob.npo_status = NDIS_STATUS_PENDING; 1432151207Swpaul m_freem(m0); 1433151207Swpaul if (m == NULL) { 1434151207Swpaul ifp->if_ierrors++; 1435151207Swpaul continue; 1436151207Swpaul } 1437151207Swpaul m0 = m; 1438126833Swpaul m0->m_pkthdr.rcvif = ifp; 1439126833Swpaul 1440126833Swpaul /* Deal with checksum offload. */ 1441126833Swpaul 1442126833Swpaul if (ifp->if_capenable & IFCAP_RXCSUM && 1443126833Swpaul p->np_ext.npe_info[ndis_tcpipcsum_info] != NULL) { 1444126833Swpaul s = (uintptr_t) 1445126833Swpaul p->np_ext.npe_info[ndis_tcpipcsum_info]; 1446126833Swpaul csum = (ndis_tcpip_csum *)&s; 1447126833Swpaul if (csum->u.ntc_rxflags & 1448126833Swpaul NDIS_RXCSUM_IP_PASSED) 1449126833Swpaul m0->m_pkthdr.csum_flags |= 1450126833Swpaul CSUM_IP_CHECKED|CSUM_IP_VALID; 1451126833Swpaul if (csum->u.ntc_rxflags & 1452126833Swpaul (NDIS_RXCSUM_TCP_PASSED | 1453126833Swpaul NDIS_RXCSUM_UDP_PASSED)) { 1454126833Swpaul m0->m_pkthdr.csum_flags |= 1455126833Swpaul CSUM_DATA_VALID|CSUM_PSEUDO_HDR; 1456126833Swpaul m0->m_pkthdr.csum_data = 0xFFFF; 1457126833Swpaul } 1458126833Swpaul } 1459126833Swpaul 1460151451Swpaul KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); 1461155311Swpaul _IF_ENQUEUE(&sc->ndis_rxqueue, m0); 1462151451Swpaul KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); 1463151451Swpaul IoQueueWorkItem(sc->ndis_inputitem, 1464151451Swpaul (io_workitem_func)ndis_inputtask_wrap, 1465151451Swpaul WORKQUEUE_CRITICAL, ifp); 1466126833Swpaul } 1467126833Swpaul } 1468126833Swpaul} 1469128546Swpaul 1470126833Swpaul/* 1471151451Swpaul * This routine is run at PASSIVE_LEVEL. We use this routine to pass 1472151451Swpaul * packets into the stack in order to avoid calling (*ifp->if_input)() 1473151451Swpaul * with any locks held (at DISPATCH_LEVEL, we'll be holding the 1474151451Swpaul * 'dispatch level' per-cpu sleep lock). 1475151451Swpaul */ 1476151451Swpaulstatic void 1477151451Swpaulndis_inputtask(dobj, arg) 1478151451Swpaul device_object *dobj; 1479151451Swpaul void *arg; 1480151451Swpaul{ 1481151451Swpaul ndis_miniport_block *block; 1482151451Swpaul struct ifnet *ifp; 1483151451Swpaul struct ndis_softc *sc; 1484151451Swpaul struct mbuf *m; 1485178354Ssam struct ieee80211com *ic; 1486178354Ssam struct ieee80211vap *vap; 1487151451Swpaul uint8_t irql; 1488151451Swpaul 1489151451Swpaul ifp = arg; 1490151451Swpaul sc = ifp->if_softc; 1491178354Ssam ic = ifp->if_l2com; 1492178354Ssam vap = TAILQ_FIRST(&ic->ic_vaps); 1493151451Swpaul block = dobj->do_devext; 1494151451Swpaul 1495151451Swpaul KeAcquireSpinLock(&sc->ndis_rxlock, &irql); 1496151451Swpaul while(1) { 1497151451Swpaul _IF_DEQUEUE(&sc->ndis_rxqueue, m); 1498151451Swpaul if (m == NULL) 1499151451Swpaul break; 1500151451Swpaul KeReleaseSpinLock(&sc->ndis_rxlock, irql); 1501197659Scokane if ((sc->ndis_80211 != 0) && (vap != NULL)) 1502178354Ssam vap->iv_deliver_data(vap, vap->iv_bss, m); 1503178354Ssam else 1504178354Ssam (*ifp->if_input)(ifp, m); 1505151451Swpaul KeAcquireSpinLock(&sc->ndis_rxlock, &irql); 1506151451Swpaul } 1507151451Swpaul KeReleaseSpinLock(&sc->ndis_rxlock, irql); 1508151451Swpaul} 1509151451Swpaul 1510151451Swpaul/* 1511123474Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 1512123474Swpaul * the list buffers. 1513123474Swpaul */ 1514144888Swpaulstatic void 1515123474Swpaulndis_txeof(adapter, packet, status) 1516123474Swpaul ndis_handle adapter; 1517123474Swpaul ndis_packet *packet; 1518123474Swpaul ndis_status status; 1519123474Swpaul 1520123474Swpaul{ 1521123474Swpaul struct ndis_softc *sc; 1522123474Swpaul ndis_miniport_block *block; 1523123474Swpaul struct ifnet *ifp; 1524123474Swpaul int idx; 1525123535Swpaul struct mbuf *m; 1526123474Swpaul 1527123474Swpaul block = (ndis_miniport_block *)adapter; 1528141524Swpaul sc = device_get_softc(block->nmb_physdeviceobj->do_devext); 1529147256Sbrooks ifp = sc->ifp; 1530123474Swpaul 1531123826Swpaul m = packet->np_m0; 1532123826Swpaul idx = packet->np_txidx; 1533123535Swpaul if (sc->ndis_sc) 1534123535Swpaul bus_dmamap_unload(sc->ndis_ttag, sc->ndis_tmaps[idx]); 1535123535Swpaul 1536123474Swpaul ndis_free_packet(packet); 1537124697Swpaul m_freem(m); 1538124697Swpaul 1539189488Sweongyo NDIS_LOCK(sc); 1540123535Swpaul sc->ndis_txarray[idx] = NULL; 1541123474Swpaul sc->ndis_txpending++; 1542123474Swpaul 1543124697Swpaul if (status == NDIS_STATUS_SUCCESS) 1544124697Swpaul ifp->if_opackets++; 1545124697Swpaul else 1546124697Swpaul ifp->if_oerrors++; 1547151207Swpaul 1548179498Scokane sc->ndis_tx_timer = 0; 1549148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1550145895Swpaul 1551189488Sweongyo NDIS_UNLOCK(sc); 1552123474Swpaul 1553151207Swpaul IoQueueWorkItem(sc->ndis_startitem, 1554151207Swpaul (io_workitem_func)ndis_starttask_wrap, 1555151207Swpaul WORKQUEUE_CRITICAL, ifp); 1556123474Swpaul} 1557123474Swpaul 1558144888Swpaulstatic void 1559123695Swpaulndis_linksts(adapter, status, sbuf, slen) 1560123695Swpaul ndis_handle adapter; 1561123695Swpaul ndis_status status; 1562123695Swpaul void *sbuf; 1563123695Swpaul uint32_t slen; 1564123695Swpaul{ 1565123695Swpaul ndis_miniport_block *block; 1566143204Swpaul struct ndis_softc *sc; 1567124005Swpaul 1568124005Swpaul block = adapter; 1569143204Swpaul sc = device_get_softc(block->nmb_physdeviceobj->do_devext); 1570151207Swpaul sc->ndis_sts = status; 1571143204Swpaul 1572151207Swpaul /* Event list is all full up, drop this one. */ 1573124005Swpaul 1574189488Sweongyo NDIS_LOCK(sc); 1575151207Swpaul if (sc->ndis_evt[sc->ndis_evtpidx].ne_sts) { 1576189488Sweongyo NDIS_UNLOCK(sc); 1577151207Swpaul return; 1578151207Swpaul } 1579151207Swpaul 1580151207Swpaul /* Cache the event. */ 1581151207Swpaul 1582151207Swpaul if (slen) { 1583151207Swpaul sc->ndis_evt[sc->ndis_evtpidx].ne_buf = malloc(slen, 1584151207Swpaul M_TEMP, M_NOWAIT); 1585151207Swpaul if (sc->ndis_evt[sc->ndis_evtpidx].ne_buf == NULL) { 1586189488Sweongyo NDIS_UNLOCK(sc); 1587151207Swpaul return; 1588151207Swpaul } 1589151207Swpaul bcopy((char *)sbuf, 1590151207Swpaul sc->ndis_evt[sc->ndis_evtpidx].ne_buf, slen); 1591151207Swpaul } 1592151207Swpaul sc->ndis_evt[sc->ndis_evtpidx].ne_sts = status; 1593151207Swpaul sc->ndis_evt[sc->ndis_evtpidx].ne_len = slen; 1594151207Swpaul NDIS_EVTINC(sc->ndis_evtpidx); 1595189488Sweongyo NDIS_UNLOCK(sc); 1596124005Swpaul} 1597124005Swpaul 1598144888Swpaulstatic void 1599124005Swpaulndis_linksts_done(adapter) 1600124005Swpaul ndis_handle adapter; 1601124005Swpaul{ 1602124005Swpaul ndis_miniport_block *block; 1603123695Swpaul struct ndis_softc *sc; 1604123695Swpaul struct ifnet *ifp; 1605123695Swpaul 1606123695Swpaul block = adapter; 1607141524Swpaul sc = device_get_softc(block->nmb_physdeviceobj->do_devext); 1608147256Sbrooks ifp = sc->ifp; 1609123695Swpaul 1610144174Swpaul if (!NDIS_INITIALIZED(sc)) 1611123695Swpaul return; 1612123695Swpaul 1613151207Swpaul switch (sc->ndis_sts) { 1614123695Swpaul case NDIS_STATUS_MEDIA_CONNECT: 1615151207Swpaul IoQueueWorkItem(sc->ndis_tickitem, 1616151207Swpaul (io_workitem_func)ndis_ticktask_wrap, 1617151207Swpaul WORKQUEUE_CRITICAL, sc); 1618151207Swpaul IoQueueWorkItem(sc->ndis_startitem, 1619151207Swpaul (io_workitem_func)ndis_starttask_wrap, 1620151207Swpaul WORKQUEUE_CRITICAL, ifp); 1621123695Swpaul break; 1622123695Swpaul case NDIS_STATUS_MEDIA_DISCONNECT: 1623127249Swpaul if (sc->ndis_link) 1624151207Swpaul IoQueueWorkItem(sc->ndis_tickitem, 1625151207Swpaul (io_workitem_func)ndis_ticktask_wrap, 1626151207Swpaul WORKQUEUE_CRITICAL, sc); 1627123695Swpaul break; 1628123695Swpaul default: 1629123695Swpaul break; 1630123695Swpaul } 1631123474Swpaul} 1632123474Swpaul 1633123474Swpaulstatic void 1634123474Swpaulndis_tick(xsc) 1635123474Swpaul void *xsc; 1636123474Swpaul{ 1637123757Swpaul struct ndis_softc *sc; 1638128780Swpaul 1639124246Swpaul sc = xsc; 1640124697Swpaul 1641179498Scokane if (sc->ndis_hang_timer && --sc->ndis_hang_timer == 0) { 1642179498Scokane IoQueueWorkItem(sc->ndis_tickitem, 1643179498Scokane (io_workitem_func)ndis_ticktask_wrap, 1644179498Scokane WORKQUEUE_CRITICAL, sc); 1645179498Scokane sc->ndis_hang_timer = sc->ndis_block->nmb_checkforhangsecs; 1646179498Scokane } 1647124409Swpaul 1648179498Scokane if (sc->ndis_tx_timer && --sc->ndis_tx_timer == 0) { 1649179498Scokane sc->ifp->if_oerrors++; 1650179498Scokane device_printf(sc->ndis_dev, "watchdog timeout\n"); 1651179498Scokane 1652179498Scokane IoQueueWorkItem(sc->ndis_resetitem, 1653179498Scokane (io_workitem_func)ndis_resettask_wrap, 1654179498Scokane WORKQUEUE_CRITICAL, sc); 1655179498Scokane IoQueueWorkItem(sc->ndis_startitem, 1656179498Scokane (io_workitem_func)ndis_starttask_wrap, 1657179498Scokane WORKQUEUE_CRITICAL, sc->ifp); 1658179498Scokane } 1659179498Scokane 1660179498Scokane callout_reset(&sc->ndis_stat_callout, hz, ndis_tick, sc); 1661124246Swpaul} 1662124246Swpaul 1663124246Swpaulstatic void 1664151207Swpaulndis_ticktask(d, xsc) 1665151207Swpaul device_object *d; 1666124246Swpaul void *xsc; 1667124246Swpaul{ 1668124246Swpaul struct ndis_softc *sc; 1669151207Swpaul struct ieee80211com *ic; 1670178354Ssam struct ieee80211vap *vap; 1671144888Swpaul ndis_checkforhang_handler hangfunc; 1672123474Swpaul uint8_t rval; 1673123474Swpaul 1674123474Swpaul sc = xsc; 1675178354Ssam ic = sc->ifp->if_l2com; 1676178354Ssam vap = TAILQ_FIRST(&ic->ic_vaps); 1677123474Swpaul 1678151625Swpaul NDIS_LOCK(sc); 1679151625Swpaul if (!NDIS_INITIALIZED(sc)) { 1680151625Swpaul NDIS_UNLOCK(sc); 1681151625Swpaul return; 1682151625Swpaul } 1683151625Swpaul NDIS_UNLOCK(sc); 1684151625Swpaul 1685141524Swpaul hangfunc = sc->ndis_chars->nmc_checkhang_func; 1686127887Swpaul 1687127887Swpaul if (hangfunc != NULL) { 1688144174Swpaul rval = MSCALL1(hangfunc, 1689144174Swpaul sc->ndis_block->nmb_miniportadapterctx); 1690127887Swpaul if (rval == TRUE) { 1691127887Swpaul ndis_reset_nic(sc); 1692127887Swpaul return; 1693127887Swpaul } 1694127887Swpaul } 1695127887Swpaul 1696124014Swpaul NDIS_LOCK(sc); 1697151207Swpaul if (sc->ndis_link == 0 && 1698151207Swpaul sc->ndis_sts == NDIS_STATUS_MEDIA_CONNECT) { 1699123695Swpaul sc->ndis_link = 1; 1700197659Scokane if ((sc->ndis_80211 != 0) && (vap != NULL)) { 1701271714Sjhb NDIS_UNLOCK(sc); 1702124409Swpaul ndis_getstate_80211(sc); 1703178354Ssam ieee80211_new_state(vap, IEEE80211_S_RUN, -1); 1704271714Sjhb NDIS_LOCK(sc); 1705271714Sjhb if_link_state_change(vap->iv_ifp, LINK_STATE_UP); 1706271714Sjhb } else 1707271714Sjhb if_link_state_change(sc->ifp, LINK_STATE_UP); 1708124409Swpaul } 1709123695Swpaul 1710151207Swpaul if (sc->ndis_link == 1 && 1711151207Swpaul sc->ndis_sts == NDIS_STATUS_MEDIA_DISCONNECT) { 1712124409Swpaul sc->ndis_link = 0; 1713271714Sjhb if ((sc->ndis_80211 != 0) && (vap != NULL)) { 1714271714Sjhb NDIS_UNLOCK(sc); 1715178354Ssam ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); 1716271714Sjhb NDIS_LOCK(sc); 1717271714Sjhb if_link_state_change(vap->iv_ifp, LINK_STATE_DOWN); 1718271714Sjhb } else 1719271714Sjhb if_link_state_change(sc->ifp, LINK_STATE_DOWN); 1720124409Swpaul } 1721124409Swpaul 1722124409Swpaul NDIS_UNLOCK(sc); 1723123474Swpaul} 1724123474Swpaul 1725123474Swpaulstatic void 1726123474Swpaulndis_map_sclist(arg, segs, nseg, mapsize, error) 1727123474Swpaul void *arg; 1728123474Swpaul bus_dma_segment_t *segs; 1729123474Swpaul int nseg; 1730123474Swpaul bus_size_t mapsize; 1731123474Swpaul int error; 1732123474Swpaul 1733123474Swpaul{ 1734123474Swpaul struct ndis_sc_list *sclist; 1735123474Swpaul int i; 1736123474Swpaul 1737123474Swpaul if (error || arg == NULL) 1738123474Swpaul return; 1739123474Swpaul 1740123474Swpaul sclist = arg; 1741123474Swpaul 1742123474Swpaul sclist->nsl_frags = nseg; 1743123474Swpaul 1744123474Swpaul for (i = 0; i < nseg; i++) { 1745123474Swpaul sclist->nsl_elements[i].nse_addr.np_quad = segs[i].ds_addr; 1746123474Swpaul sclist->nsl_elements[i].nse_len = segs[i].ds_len; 1747123474Swpaul } 1748123474Swpaul} 1749123474Swpaul 1750178354Ssamstatic int 1751178354Ssamndis_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 1752178354Ssam const struct ieee80211_bpf_params *params) 1753178354Ssam{ 1754178354Ssam /* no support; just discard */ 1755178354Ssam m_freem(m); 1756178354Ssam ieee80211_free_node(ni); 1757198786Srpaulo return (0); 1758178354Ssam} 1759178354Ssam 1760124246Swpaulstatic void 1761185485Ssamndis_update_mcast(struct ifnet *ifp) 1762185485Ssam{ 1763185485Ssam struct ndis_softc *sc = ifp->if_softc; 1764185485Ssam 1765185485Ssam ndis_setmulti(sc); 1766185485Ssam} 1767185485Ssam 1768185485Ssamstatic void 1769185485Ssamndis_update_promisc(struct ifnet *ifp) 1770185485Ssam{ 1771185485Ssam /* not supported */ 1772185485Ssam} 1773185485Ssam 1774185485Ssamstatic void 1775151207Swpaulndis_starttask(d, arg) 1776151207Swpaul device_object *d; 1777124246Swpaul void *arg; 1778124246Swpaul{ 1779124246Swpaul struct ifnet *ifp; 1780124246Swpaul 1781124246Swpaul ifp = arg; 1782145895Swpaul 1783132986Smlaier if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 1784124246Swpaul ndis_start(ifp); 1785124246Swpaul} 1786124246Swpaul 1787123474Swpaul/* 1788123474Swpaul * Main transmit routine. To make NDIS drivers happy, we need to 1789123474Swpaul * transform mbuf chains into NDIS packets and feed them to the 1790123474Swpaul * send packet routines. Most drivers allow you to send several 1791123474Swpaul * packets at once (up to the maxpkts limit). Unfortunately, rather 1792123474Swpaul * that accepting them in the form of a linked list, they expect 1793123474Swpaul * a contiguous array of pointers to packets. 1794123474Swpaul * 1795123474Swpaul * For those drivers which use the NDIS scatter/gather DMA mechanism, 1796123474Swpaul * we need to perform busdma work here. Those that use map registers 1797123474Swpaul * will do the mapping themselves on a buffer by buffer basis. 1798123474Swpaul */ 1799123474Swpaulstatic void 1800123474Swpaulndis_start(ifp) 1801123474Swpaul struct ifnet *ifp; 1802123474Swpaul{ 1803123474Swpaul struct ndis_softc *sc; 1804123474Swpaul struct mbuf *m = NULL; 1805123474Swpaul ndis_packet **p0 = NULL, *p = NULL; 1806124821Swpaul ndis_tcpip_csum *csum; 1807141963Swpaul int pcnt = 0, status; 1808123474Swpaul 1809123474Swpaul sc = ifp->if_softc; 1810123474Swpaul 1811123537Swpaul NDIS_LOCK(sc); 1812148887Srwatson if (!sc->ndis_link || ifp->if_drv_flags & IFF_DRV_OACTIVE) { 1813123695Swpaul NDIS_UNLOCK(sc); 1814123695Swpaul return; 1815123695Swpaul } 1816123695Swpaul 1817123474Swpaul p0 = &sc->ndis_txarray[sc->ndis_txidx]; 1818123474Swpaul 1819123474Swpaul while(sc->ndis_txpending) { 1820132986Smlaier IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 1821123474Swpaul if (m == NULL) 1822123474Swpaul break; 1823123474Swpaul 1824141963Swpaul NdisAllocatePacket(&status, 1825141963Swpaul &sc->ndis_txarray[sc->ndis_txidx], sc->ndis_txpool); 1826123535Swpaul 1827141963Swpaul if (status != NDIS_STATUS_SUCCESS) 1828141963Swpaul break; 1829141963Swpaul 1830123474Swpaul if (ndis_mtop(m, &sc->ndis_txarray[sc->ndis_txidx])) { 1831132986Smlaier IFQ_DRV_PREPEND(&ifp->if_snd, m); 1832123474Swpaul NDIS_UNLOCK(sc); 1833123474Swpaul return; 1834123474Swpaul } 1835123474Swpaul 1836123474Swpaul /* 1837123474Swpaul * Save pointer to original mbuf 1838123474Swpaul * so we can free it later. 1839123474Swpaul */ 1840123474Swpaul 1841124014Swpaul p = sc->ndis_txarray[sc->ndis_txidx]; 1842124014Swpaul p->np_txidx = sc->ndis_txidx; 1843124014Swpaul p->np_m0 = m; 1844124014Swpaul p->np_oob.npo_status = NDIS_STATUS_PENDING; 1845123474Swpaul 1846123474Swpaul /* 1847123474Swpaul * Do scatter/gather processing, if driver requested it. 1848123474Swpaul */ 1849123474Swpaul if (sc->ndis_sc) { 1850123474Swpaul bus_dmamap_load_mbuf(sc->ndis_ttag, 1851123474Swpaul sc->ndis_tmaps[sc->ndis_txidx], m, 1852123474Swpaul ndis_map_sclist, &p->np_sclist, BUS_DMA_NOWAIT); 1853123474Swpaul bus_dmamap_sync(sc->ndis_ttag, 1854123474Swpaul sc->ndis_tmaps[sc->ndis_txidx], 1855123474Swpaul BUS_DMASYNC_PREREAD); 1856123474Swpaul p->np_ext.npe_info[ndis_sclist_info] = &p->np_sclist; 1857123474Swpaul } 1858123474Swpaul 1859124821Swpaul /* Handle checksum offload. */ 1860124821Swpaul 1861124821Swpaul if (ifp->if_capenable & IFCAP_TXCSUM && 1862124821Swpaul m->m_pkthdr.csum_flags) { 1863124821Swpaul csum = (ndis_tcpip_csum *) 1864124821Swpaul &p->np_ext.npe_info[ndis_tcpipcsum_info]; 1865124821Swpaul csum->u.ntc_txflags = NDIS_TXCSUM_DO_IPV4; 1866124821Swpaul if (m->m_pkthdr.csum_flags & CSUM_IP) 1867124821Swpaul csum->u.ntc_txflags |= NDIS_TXCSUM_DO_IP; 1868124821Swpaul if (m->m_pkthdr.csum_flags & CSUM_TCP) 1869124821Swpaul csum->u.ntc_txflags |= NDIS_TXCSUM_DO_TCP; 1870124821Swpaul if (m->m_pkthdr.csum_flags & CSUM_UDP) 1871124821Swpaul csum->u.ntc_txflags |= NDIS_TXCSUM_DO_UDP; 1872124821Swpaul p->np_private.npp_flags = NDIS_PROTOCOL_ID_TCP_IP; 1873124821Swpaul } 1874124821Swpaul 1875123474Swpaul NDIS_INC(sc); 1876123474Swpaul sc->ndis_txpending--; 1877123474Swpaul 1878123474Swpaul pcnt++; 1879123474Swpaul 1880123474Swpaul /* 1881123474Swpaul * If there's a BPF listener, bounce a copy of this frame 1882123474Swpaul * to him. 1883123474Swpaul */ 1884178354Ssam if (!sc->ndis_80211) /* XXX handle 80211 */ 1885178354Ssam BPF_MTAP(ifp, m); 1886123474Swpaul 1887123474Swpaul /* 1888123474Swpaul * The array that p0 points to must appear contiguous, 1889123474Swpaul * so we must not wrap past the end of sc->ndis_txarray[]. 1890123474Swpaul * If it looks like we're about to wrap, break out here 1891123474Swpaul * so the this batch of packets can be transmitted, then 1892123474Swpaul * wait for txeof to ask us to send the rest. 1893123474Swpaul */ 1894123474Swpaul if (sc->ndis_txidx == 0) 1895123474Swpaul break; 1896123474Swpaul } 1897123474Swpaul 1898136677Sle if (pcnt == 0) { 1899136677Sle NDIS_UNLOCK(sc); 1900136269Smlaier return; 1901136677Sle } 1902136269Smlaier 1903123474Swpaul if (sc->ndis_txpending == 0) 1904148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1905123474Swpaul 1906123474Swpaul /* 1907123474Swpaul * Set a timeout in case the chip goes out to lunch. 1908123474Swpaul */ 1909179498Scokane sc->ndis_tx_timer = 5; 1910123474Swpaul 1911123537Swpaul NDIS_UNLOCK(sc); 1912123537Swpaul 1913151207Swpaul /* 1914151207Swpaul * According to NDIS documentation, if a driver exports 1915151207Swpaul * a MiniportSendPackets() routine, we prefer that over 1916151207Swpaul * a MiniportSend() routine (which sends just a single 1917151207Swpaul * packet). 1918151207Swpaul */ 1919151207Swpaul if (sc->ndis_chars->nmc_sendmulti_func != NULL) 1920151207Swpaul ndis_send_packets(sc, p0, pcnt); 1921151207Swpaul else 1922125377Swpaul ndis_send_packet(sc, p); 1923123537Swpaul 1924123474Swpaul return; 1925123474Swpaul} 1926123474Swpaul 1927123474Swpaulstatic void 1928123474Swpaulndis_init(xsc) 1929123474Swpaul void *xsc; 1930123474Swpaul{ 1931123474Swpaul struct ndis_softc *sc = xsc; 1932147256Sbrooks struct ifnet *ifp = sc->ifp; 1933178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1934151207Swpaul int i, len, error; 1935123474Swpaul 1936123474Swpaul /* 1937125068Swpaul * Avoid reintializing the link unnecessarily. 1938125068Swpaul * This should be dealt with in a better way by 1939125068Swpaul * fixing the upper layer modules so they don't 1940125068Swpaul * call ifp->if_init() quite as often. 1941125068Swpaul */ 1942178354Ssam if (sc->ndis_link) 1943125068Swpaul return; 1944125068Swpaul 1945125068Swpaul /* 1946123474Swpaul * Cancel pending I/O and free all RX/TX buffers. 1947123474Swpaul */ 1948123474Swpaul ndis_stop(sc); 1949151207Swpaul 1950186507Sweongyo if (!(sc->ndis_iftype == PNPBus && ndisusb_halt == 0)) { 1951186507Sweongyo error = ndis_init_nic(sc); 1952186507Sweongyo if (error != 0) { 1953186507Sweongyo device_printf(sc->ndis_dev, 1954186507Sweongyo "failed to initialize the device: %d\n", error); 1955186507Sweongyo return; 1956186507Sweongyo } 1957186507Sweongyo } 1958123474Swpaul 1959123474Swpaul /* Init our MAC address */ 1960123474Swpaul 1961123474Swpaul /* Program the packet filter */ 1962123474Swpaul 1963124709Swpaul sc->ndis_filter = NDIS_PACKET_TYPE_DIRECTED; 1964123474Swpaul 1965123474Swpaul if (ifp->if_flags & IFF_BROADCAST) 1966124709Swpaul sc->ndis_filter |= NDIS_PACKET_TYPE_BROADCAST; 1967123474Swpaul 1968123474Swpaul if (ifp->if_flags & IFF_PROMISC) 1969124709Swpaul sc->ndis_filter |= NDIS_PACKET_TYPE_PROMISCUOUS; 1970123474Swpaul 1971151207Swpaul len = sizeof(sc->ndis_filter); 1972123474Swpaul 1973123474Swpaul error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, 1974151207Swpaul &sc->ndis_filter, &len); 1975123474Swpaul 1976123474Swpaul if (error) 1977198786Srpaulo device_printf(sc->ndis_dev, "set filter failed: %d\n", error); 1978123474Swpaul 1979124709Swpaul /* 1980151207Swpaul * Set lookahead. 1981151207Swpaul */ 1982151207Swpaul i = ifp->if_mtu; 1983151207Swpaul len = sizeof(i); 1984151207Swpaul ndis_set_info(sc, OID_GEN_CURRENT_LOOKAHEAD, &i, &len); 1985151207Swpaul 1986151207Swpaul /* 1987124709Swpaul * Program the multicast filter, if necessary. 1988124709Swpaul */ 1989124709Swpaul ndis_setmulti(sc); 1990123535Swpaul 1991124821Swpaul /* Setup task offload. */ 1992124821Swpaul ndis_set_offload(sc); 1993178929Sthompsa 1994124709Swpaul NDIS_LOCK(sc); 1995124709Swpaul 1996124709Swpaul sc->ndis_txidx = 0; 1997124709Swpaul sc->ndis_txpending = sc->ndis_maxpkts; 1998124709Swpaul sc->ndis_link = 0; 1999124709Swpaul 2000151207Swpaul if_link_state_change(sc->ifp, LINK_STATE_UNKNOWN); 2001151207Swpaul 2002148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 2003148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2004179498Scokane sc->ndis_tx_timer = 0; 2005123474Swpaul 2006124165Swpaul /* 2007124165Swpaul * Some drivers don't set this value. The NDIS spec says 2008127336Swpaul * the default checkforhang timeout is "approximately 2 2009127336Swpaul * seconds." We use 3 seconds, because it seems for some 2010127336Swpaul * drivers, exactly 2 seconds is too fast. 2011124165Swpaul */ 2012141524Swpaul if (sc->ndis_block->nmb_checkforhangsecs == 0) 2013141524Swpaul sc->ndis_block->nmb_checkforhangsecs = 3; 2014124165Swpaul 2015179498Scokane sc->ndis_hang_timer = sc->ndis_block->nmb_checkforhangsecs; 2016179498Scokane callout_reset(&sc->ndis_stat_callout, hz, ndis_tick, sc); 2017179498Scokane NDIS_UNLOCK(sc); 2018123474Swpaul 2019179498Scokane /* XXX force handling */ 2020191163Sthompsa if (sc->ndis_80211) 2021191163Sthompsa ieee80211_start_all(ic); /* start all vap's */ 2022123474Swpaul} 2023123474Swpaul 2024123474Swpaul/* 2025123474Swpaul * Set media options. 2026123474Swpaul */ 2027123474Swpaulstatic int 2028123474Swpaulndis_ifmedia_upd(ifp) 2029123474Swpaul struct ifnet *ifp; 2030123474Swpaul{ 2031123474Swpaul struct ndis_softc *sc; 2032123474Swpaul 2033123474Swpaul sc = ifp->if_softc; 2034123474Swpaul 2035131750Swpaul if (NDIS_INITIALIZED(sc)) 2036123474Swpaul ndis_init(sc); 2037123474Swpaul 2038198786Srpaulo return (0); 2039123474Swpaul} 2040123474Swpaul 2041123474Swpaul/* 2042123474Swpaul * Report current media status. 2043123474Swpaul */ 2044123474Swpaulstatic void 2045123474Swpaulndis_ifmedia_sts(ifp, ifmr) 2046123474Swpaul struct ifnet *ifp; 2047123474Swpaul struct ifmediareq *ifmr; 2048123474Swpaul{ 2049123474Swpaul struct ndis_softc *sc; 2050123474Swpaul uint32_t media_info; 2051123474Swpaul ndis_media_state linkstate; 2052198786Srpaulo int len; 2053123474Swpaul 2054123848Swpaul ifmr->ifm_status = IFM_AVALID; 2055123848Swpaul ifmr->ifm_active = IFM_ETHER; 2056131750Swpaul sc = ifp->if_softc; 2057123848Swpaul 2058131750Swpaul if (!NDIS_INITIALIZED(sc)) 2059123848Swpaul return; 2060123848Swpaul 2061123474Swpaul len = sizeof(linkstate); 2062198786Srpaulo ndis_get_info(sc, OID_GEN_MEDIA_CONNECT_STATUS, 2063123474Swpaul (void *)&linkstate, &len); 2064123474Swpaul 2065123474Swpaul len = sizeof(media_info); 2066198786Srpaulo ndis_get_info(sc, OID_GEN_LINK_SPEED, 2067123474Swpaul (void *)&media_info, &len); 2068123474Swpaul 2069123474Swpaul if (linkstate == nmc_connected) 2070123474Swpaul ifmr->ifm_status |= IFM_ACTIVE; 2071123474Swpaul 2072198786Srpaulo switch (media_info) { 2073123474Swpaul case 100000: 2074123474Swpaul ifmr->ifm_active |= IFM_10_T; 2075123474Swpaul break; 2076123474Swpaul case 1000000: 2077123474Swpaul ifmr->ifm_active |= IFM_100_TX; 2078123474Swpaul break; 2079123474Swpaul case 10000000: 2080123474Swpaul ifmr->ifm_active |= IFM_1000_T; 2081123474Swpaul break; 2082123474Swpaul default: 2083124060Swpaul device_printf(sc->ndis_dev, "unknown speed: %d\n", media_info); 2084123474Swpaul break; 2085123474Swpaul } 2086123474Swpaul} 2087123474Swpaul 2088151207Swpaulstatic int 2089151207Swpaulndis_set_cipher(sc, cipher) 2090151207Swpaul struct ndis_softc *sc; 2091151207Swpaul int cipher; 2092151207Swpaul{ 2093151207Swpaul struct ieee80211com *ic; 2094151207Swpaul int rval = 0, len; 2095151207Swpaul uint32_t arg, save; 2096151207Swpaul 2097178354Ssam ic = sc->ifp->if_l2com; 2098151207Swpaul 2099151207Swpaul len = sizeof(arg); 2100151207Swpaul 2101213778Srpaulo if (cipher == WPA_CSE_WEP40 || cipher == WPA_CSE_WEP104) { 2102178354Ssam if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP)) 2103198786Srpaulo return (ENOTSUP); 2104151207Swpaul arg = NDIS_80211_WEPSTAT_ENC1ENABLED; 2105151207Swpaul } 2106151207Swpaul 2107151207Swpaul if (cipher == WPA_CSE_TKIP) { 2108178354Ssam if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP)) 2109198786Srpaulo return (ENOTSUP); 2110151207Swpaul arg = NDIS_80211_WEPSTAT_ENC2ENABLED; 2111151207Swpaul } 2112151207Swpaul 2113151207Swpaul if (cipher == WPA_CSE_CCMP) { 2114178354Ssam if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_AES_CCM)) 2115198786Srpaulo return (ENOTSUP); 2116151207Swpaul arg = NDIS_80211_WEPSTAT_ENC3ENABLED; 2117151207Swpaul } 2118151207Swpaul 2119171390Sthompsa DPRINTF(("Setting cipher to %d\n", arg)); 2120151207Swpaul save = arg; 2121151207Swpaul rval = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); 2122151207Swpaul 2123151207Swpaul if (rval) 2124198786Srpaulo return (rval); 2125151207Swpaul 2126151207Swpaul /* Check that the cipher was set correctly. */ 2127151207Swpaul 2128151207Swpaul len = sizeof(save); 2129151207Swpaul rval = ndis_get_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); 2130151207Swpaul 2131151207Swpaul if (rval != 0 || arg != save) 2132198786Srpaulo return (ENODEV); 2133151207Swpaul 2134198786Srpaulo return (0); 2135151207Swpaul} 2136151207Swpaul 2137151207Swpaul/* 2138151207Swpaul * WPA is hairy to set up. Do the work in a separate routine 2139151207Swpaul * so we don't clutter the setstate function too much. 2140151207Swpaul * Important yet undocumented fact: first we have to set the 2141151207Swpaul * authentication mode, _then_ we enable the ciphers. If one 2142151207Swpaul * of the WPA authentication modes isn't enabled, the driver 2143151207Swpaul * might not permit the TKIP or AES ciphers to be selected. 2144151207Swpaul */ 2145151207Swpaulstatic int 2146178354Ssamndis_set_wpa(sc, ie, ielen) 2147151207Swpaul struct ndis_softc *sc; 2148178354Ssam void *ie; 2149178354Ssam int ielen; 2150151207Swpaul{ 2151151207Swpaul struct ieee80211_ie_wpa *w; 2152151207Swpaul struct ndis_ie *n; 2153151207Swpaul char *pos; 2154151207Swpaul uint32_t arg; 2155151207Swpaul int i; 2156151207Swpaul 2157151207Swpaul /* 2158151207Swpaul * Apparently, the only way for us to know what ciphers 2159151207Swpaul * and key management/authentication mode to use is for 2160151207Swpaul * us to inspect the optional information element (IE) 2161151207Swpaul * stored in the 802.11 state machine. This IE should be 2162151207Swpaul * supplied by the WPA supplicant. 2163151207Swpaul */ 2164151207Swpaul 2165178354Ssam w = (struct ieee80211_ie_wpa *)ie; 2166151207Swpaul 2167151207Swpaul /* Check for the right kind of IE. */ 2168171390Sthompsa if (w->wpa_id != IEEE80211_ELEMID_VENDOR) { 2169171390Sthompsa DPRINTF(("Incorrect IE type %d\n", w->wpa_id)); 2170198786Srpaulo return (EINVAL); 2171171390Sthompsa } 2172151207Swpaul 2173151207Swpaul /* Skip over the ucast cipher OIDs. */ 2174151207Swpaul pos = (char *)&w->wpa_uciphers[0]; 2175151207Swpaul pos += w->wpa_uciphercnt * sizeof(struct ndis_ie); 2176151207Swpaul 2177151207Swpaul /* Skip over the authmode count. */ 2178151207Swpaul pos += sizeof(u_int16_t); 2179151207Swpaul 2180151207Swpaul /* 2181151207Swpaul * Check for the authentication modes. I'm 2182151207Swpaul * pretty sure there's only supposed to be one. 2183151207Swpaul */ 2184151207Swpaul 2185151207Swpaul n = (struct ndis_ie *)pos; 2186151207Swpaul if (n->ni_val == WPA_ASE_NONE) 2187151207Swpaul arg = NDIS_80211_AUTHMODE_WPANONE; 2188151207Swpaul 2189151207Swpaul if (n->ni_val == WPA_ASE_8021X_UNSPEC) 2190151207Swpaul arg = NDIS_80211_AUTHMODE_WPA; 2191151207Swpaul 2192151207Swpaul if (n->ni_val == WPA_ASE_8021X_PSK) 2193151207Swpaul arg = NDIS_80211_AUTHMODE_WPAPSK; 2194151207Swpaul 2195171390Sthompsa DPRINTF(("Setting WPA auth mode to %d\n", arg)); 2196151207Swpaul i = sizeof(arg); 2197151207Swpaul if (ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &i)) 2198198786Srpaulo return (ENOTSUP); 2199151207Swpaul i = sizeof(arg); 2200151207Swpaul ndis_get_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &i); 2201151207Swpaul 2202151207Swpaul /* Now configure the desired ciphers. */ 2203151207Swpaul 2204151207Swpaul /* First, set up the multicast group cipher. */ 2205151207Swpaul n = (struct ndis_ie *)&w->wpa_mcipher[0]; 2206151207Swpaul 2207151207Swpaul if (ndis_set_cipher(sc, n->ni_val)) 2208198786Srpaulo return (ENOTSUP); 2209151207Swpaul 2210151207Swpaul /* Now start looking around for the unicast ciphers. */ 2211151207Swpaul pos = (char *)&w->wpa_uciphers[0]; 2212151207Swpaul n = (struct ndis_ie *)pos; 2213151207Swpaul 2214151207Swpaul for (i = 0; i < w->wpa_uciphercnt; i++) { 2215151207Swpaul if (ndis_set_cipher(sc, n->ni_val)) 2216198786Srpaulo return (ENOTSUP); 2217151207Swpaul n++; 2218151207Swpaul } 2219151207Swpaul 2220198786Srpaulo return (0); 2221151207Swpaul} 2222151207Swpaul 2223123695Swpaulstatic void 2224201620Srpaulondis_media_status(struct ifnet *ifp, struct ifmediareq *imr) 2225201620Srpaulo{ 2226201620Srpaulo struct ieee80211vap *vap = ifp->if_softc; 2227201620Srpaulo struct ndis_softc *sc = vap->iv_ic->ic_ifp->if_softc; 2228201620Srpaulo uint32_t txrate; 2229201644Srpaulo int len; 2230201620Srpaulo 2231201620Srpaulo if (!NDIS_INITIALIZED(sc)) 2232201620Srpaulo return; 2233201620Srpaulo 2234201620Srpaulo len = sizeof(txrate); 2235201620Srpaulo if (ndis_get_info(sc, OID_GEN_LINK_SPEED, &txrate, &len) == 0) 2236201620Srpaulo vap->iv_bss->ni_txrate = txrate / 5000; 2237201620Srpaulo ieee80211_media_status(ifp, imr); 2238201620Srpaulo} 2239201620Srpaulo 2240201620Srpaulostatic void 2241123695Swpaulndis_setstate_80211(sc) 2242123695Swpaul struct ndis_softc *sc; 2243123695Swpaul{ 2244123695Swpaul struct ieee80211com *ic; 2245194432Scokane struct ieee80211vap *vap; 2246151207Swpaul ndis_80211_macaddr bssid; 2247124409Swpaul ndis_80211_config config; 2248178929Sthompsa int rval = 0, len; 2249123695Swpaul uint32_t arg; 2250123695Swpaul struct ifnet *ifp; 2251123695Swpaul 2252147256Sbrooks ifp = sc->ifp; 2253178354Ssam ic = ifp->if_l2com; 2254194432Scokane vap = TAILQ_FIRST(&ic->ic_vaps); 2255123695Swpaul 2256171390Sthompsa if (!NDIS_INITIALIZED(sc)) { 2257171390Sthompsa DPRINTF(("%s: NDIS not initialized\n", __func__)); 2258123695Swpaul return; 2259171390Sthompsa } 2260123695Swpaul 2261151207Swpaul /* Disassociate and turn off radio. */ 2262151207Swpaul len = sizeof(arg); 2263151207Swpaul arg = 1; 2264151207Swpaul ndis_set_info(sc, OID_802_11_DISASSOCIATE, &arg, &len); 2265151207Swpaul 2266123695Swpaul /* Set network infrastructure mode. */ 2267123695Swpaul 2268123695Swpaul len = sizeof(arg); 2269178929Sthompsa if (ic->ic_opmode == IEEE80211_M_IBSS) 2270123695Swpaul arg = NDIS_80211_NET_INFRA_IBSS; 2271123695Swpaul else 2272123695Swpaul arg = NDIS_80211_NET_INFRA_BSS; 2273123695Swpaul 2274123695Swpaul rval = ndis_set_info(sc, OID_802_11_INFRASTRUCTURE_MODE, &arg, &len); 2275123695Swpaul 2276123695Swpaul if (rval) 2277124060Swpaul device_printf (sc->ndis_dev, "set infra failed: %d\n", rval); 2278123695Swpaul 2279151207Swpaul /* Set power management */ 2280151207Swpaul len = sizeof(arg); 2281194432Scokane if (vap->iv_flags & IEEE80211_F_PMGTON) 2282151207Swpaul arg = NDIS_80211_POWERMODE_FAST_PSP; 2283151207Swpaul else 2284151207Swpaul arg = NDIS_80211_POWERMODE_CAM; 2285151207Swpaul ndis_set_info(sc, OID_802_11_POWER_MODE, &arg, &len); 2286151207Swpaul 2287187104Sthompsa /* Set TX power */ 2288189550Ssam if ((ic->ic_caps & IEEE80211_C_TXPMGT) && 2289189550Ssam ic->ic_txpowlimit < (sizeof(dBm2mW) / sizeof(dBm2mW[0]))) { 2290189550Ssam arg = dBm2mW[ic->ic_txpowlimit]; 2291187104Sthompsa len = sizeof(arg); 2292187104Sthompsa ndis_set_info(sc, OID_802_11_TX_POWER_LEVEL, &arg, &len); 2293187104Sthompsa } 2294187104Sthompsa 2295151207Swpaul /* 2296151207Swpaul * Default encryption mode to off, authentication 2297151207Swpaul * to open and privacy to 'accept everything.' 2298151207Swpaul */ 2299151207Swpaul len = sizeof(arg); 2300151207Swpaul arg = NDIS_80211_WEPSTAT_DISABLED; 2301151207Swpaul ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); 2302151207Swpaul 2303151207Swpaul len = sizeof(arg); 2304151207Swpaul arg = NDIS_80211_AUTHMODE_OPEN; 2305151207Swpaul ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); 2306151207Swpaul 2307151207Swpaul /* 2308198786Srpaulo * Note that OID_802_11_PRIVACY_FILTER is optional: 2309151207Swpaul * not all drivers implement it. 2310151207Swpaul */ 2311151207Swpaul len = sizeof(arg); 2312151207Swpaul arg = NDIS_80211_PRIVFILT_8021XWEP; 2313151207Swpaul ndis_set_info(sc, OID_802_11_PRIVACY_FILTER, &arg, &len); 2314151207Swpaul 2315178929Sthompsa len = sizeof(config); 2316178929Sthompsa bzero((char *)&config, len); 2317178929Sthompsa config.nc_length = len; 2318178929Sthompsa config.nc_fhconfig.ncf_length = sizeof(ndis_80211_config_fh); 2319178929Sthompsa rval = ndis_get_info(sc, OID_802_11_CONFIGURATION, &config, &len); 2320178929Sthompsa 2321178929Sthompsa /* 2322178929Sthompsa * Some drivers expect us to initialize these values, so 2323178929Sthompsa * provide some defaults. 2324178929Sthompsa */ 2325178929Sthompsa 2326178929Sthompsa if (config.nc_beaconperiod == 0) 2327178929Sthompsa config.nc_beaconperiod = 100; 2328178929Sthompsa if (config.nc_atimwin == 0) 2329178929Sthompsa config.nc_atimwin = 100; 2330178929Sthompsa if (config.nc_fhconfig.ncf_dwelltime == 0) 2331178929Sthompsa config.nc_fhconfig.ncf_dwelltime = 200; 2332178929Sthompsa if (rval == 0 && ic->ic_bsschan != IEEE80211_CHAN_ANYC) { 2333178929Sthompsa int chan, chanflag; 2334178929Sthompsa 2335178929Sthompsa chan = ieee80211_chan2ieee(ic, ic->ic_bsschan); 2336178929Sthompsa chanflag = config.nc_dsconfig > 2500000 ? IEEE80211_CHAN_2GHZ : 2337178929Sthompsa IEEE80211_CHAN_5GHZ; 2338178929Sthompsa if (chan != ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0)) { 2339178929Sthompsa config.nc_dsconfig = 2340178929Sthompsa ic->ic_bsschan->ic_freq * 1000; 2341178929Sthompsa len = sizeof(config); 2342178929Sthompsa config.nc_length = len; 2343178929Sthompsa config.nc_fhconfig.ncf_length = 2344178929Sthompsa sizeof(ndis_80211_config_fh); 2345178929Sthompsa DPRINTF(("Setting channel to %ukHz\n", config.nc_dsconfig)); 2346178929Sthompsa rval = ndis_set_info(sc, OID_802_11_CONFIGURATION, 2347178929Sthompsa &config, &len); 2348178929Sthompsa if (rval) 2349178929Sthompsa device_printf(sc->ndis_dev, "couldn't change " 2350178929Sthompsa "DS config to %ukHz: %d\n", 2351178929Sthompsa config.nc_dsconfig, rval); 2352178929Sthompsa } 2353178929Sthompsa } else if (rval) 2354178929Sthompsa device_printf(sc->ndis_dev, "couldn't retrieve " 2355178929Sthompsa "channel info: %d\n", rval); 2356178929Sthompsa 2357178929Sthompsa /* Set the BSSID to our value so the driver doesn't associate */ 2358178929Sthompsa len = IEEE80211_ADDR_LEN; 2359190526Ssam bcopy(IF_LLADDR(ifp), bssid, len); 2360178929Sthompsa DPRINTF(("Setting BSSID to %6D\n", (uint8_t *)&bssid, ":")); 2361178929Sthompsa rval = ndis_set_info(sc, OID_802_11_BSSID, &bssid, &len); 2362178929Sthompsa if (rval) 2363178929Sthompsa device_printf(sc->ndis_dev, 2364178929Sthompsa "setting BSSID failed: %d\n", rval); 2365178929Sthompsa} 2366178929Sthompsa 2367178929Sthompsastatic void 2368178929Sthompsandis_auth_and_assoc(sc, vap) 2369178929Sthompsa struct ndis_softc *sc; 2370178929Sthompsa struct ieee80211vap *vap; 2371178929Sthompsa{ 2372178929Sthompsa struct ieee80211com *ic; 2373178929Sthompsa struct ieee80211_node *ni; 2374178929Sthompsa ndis_80211_ssid ssid; 2375178929Sthompsa ndis_80211_macaddr bssid; 2376178929Sthompsa ndis_80211_wep wep; 2377178929Sthompsa int i, rval = 0, len, error; 2378178929Sthompsa uint32_t arg; 2379178929Sthompsa struct ifnet *ifp; 2380178929Sthompsa 2381178929Sthompsa ifp = sc->ifp; 2382178929Sthompsa ic = ifp->if_l2com; 2383178929Sthompsa ni = vap->iv_bss; 2384178929Sthompsa 2385178929Sthompsa if (!NDIS_INITIALIZED(sc)) { 2386178929Sthompsa DPRINTF(("%s: NDIS not initialized\n", __func__)); 2387178929Sthompsa return; 2388178929Sthompsa } 2389178929Sthompsa 2390178929Sthompsa /* Initial setup */ 2391178929Sthompsa ndis_setstate_80211(sc); 2392178929Sthompsa 2393178929Sthompsa /* Set network infrastructure mode. */ 2394178929Sthompsa 2395178929Sthompsa len = sizeof(arg); 2396178929Sthompsa if (vap->iv_opmode == IEEE80211_M_IBSS) 2397178929Sthompsa arg = NDIS_80211_NET_INFRA_IBSS; 2398178929Sthompsa else 2399178929Sthompsa arg = NDIS_80211_NET_INFRA_BSS; 2400178929Sthompsa 2401178929Sthompsa rval = ndis_set_info(sc, OID_802_11_INFRASTRUCTURE_MODE, &arg, &len); 2402178929Sthompsa 2403178929Sthompsa if (rval) 2404178929Sthompsa device_printf (sc->ndis_dev, "set infra failed: %d\n", rval); 2405178929Sthompsa 2406178929Sthompsa /* Set RTS threshold */ 2407178929Sthompsa 2408178929Sthompsa len = sizeof(arg); 2409178929Sthompsa arg = vap->iv_rtsthreshold; 2410178929Sthompsa ndis_set_info(sc, OID_802_11_RTS_THRESHOLD, &arg, &len); 2411178929Sthompsa 2412178929Sthompsa /* Set fragmentation threshold */ 2413178929Sthompsa 2414178929Sthompsa len = sizeof(arg); 2415178929Sthompsa arg = vap->iv_fragthreshold; 2416178929Sthompsa ndis_set_info(sc, OID_802_11_FRAGMENTATION_THRESHOLD, &arg, &len); 2417178929Sthompsa 2418124005Swpaul /* Set WEP */ 2419124005Swpaul 2420178354Ssam if (vap->iv_flags & IEEE80211_F_PRIVACY && 2421178354Ssam !(vap->iv_flags & IEEE80211_F_WPA)) { 2422198786Srpaulo int keys_set = 0; 2423151207Swpaul 2424178354Ssam if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { 2425165573Sjkim len = sizeof(arg); 2426165573Sjkim arg = NDIS_80211_AUTHMODE_SHARED; 2427171390Sthompsa DPRINTF(("Setting shared auth\n")); 2428165573Sjkim ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, 2429165573Sjkim &arg, &len); 2430165573Sjkim } 2431124005Swpaul for (i = 0; i < IEEE80211_WEP_NKID; i++) { 2432178354Ssam if (vap->iv_nw_keys[i].wk_keylen) { 2433178354Ssam if (vap->iv_nw_keys[i].wk_cipher->ic_cipher != 2434151207Swpaul IEEE80211_CIPHER_WEP) 2435151207Swpaul continue; 2436124005Swpaul bzero((char *)&wep, sizeof(wep)); 2437178354Ssam wep.nw_keylen = vap->iv_nw_keys[i].wk_keylen; 2438151207Swpaul 2439151207Swpaul /* 2440151207Swpaul * 5, 13 and 16 are the only valid 2441198786Srpaulo * key lengths. Anything in between 2442198786Srpaulo * will be zero padded out to the 2443198786Srpaulo * next highest boundary. 2444151207Swpaul */ 2445178354Ssam if (vap->iv_nw_keys[i].wk_keylen < 5) 2446124005Swpaul wep.nw_keylen = 5; 2447178354Ssam else if (vap->iv_nw_keys[i].wk_keylen > 5 && 2448178354Ssam vap->iv_nw_keys[i].wk_keylen < 13) 2449124005Swpaul wep.nw_keylen = 13; 2450178354Ssam else if (vap->iv_nw_keys[i].wk_keylen > 13 && 2451178354Ssam vap->iv_nw_keys[i].wk_keylen < 16) 2452151207Swpaul wep.nw_keylen = 16; 2453151207Swpaul 2454124005Swpaul wep.nw_keyidx = i; 2455124005Swpaul wep.nw_length = (sizeof(uint32_t) * 3) 2456124005Swpaul + wep.nw_keylen; 2457178354Ssam if (i == vap->iv_def_txkey) 2458124005Swpaul wep.nw_keyidx |= NDIS_80211_WEPKEY_TX; 2459178354Ssam bcopy(vap->iv_nw_keys[i].wk_key, 2460124005Swpaul wep.nw_keydata, wep.nw_length); 2461124005Swpaul len = sizeof(wep); 2462171390Sthompsa DPRINTF(("Setting WEP key %d\n", i)); 2463124005Swpaul rval = ndis_set_info(sc, 2464124005Swpaul OID_802_11_ADD_WEP, &wep, &len); 2465124005Swpaul if (rval) 2466124060Swpaul device_printf(sc->ndis_dev, 2467124060Swpaul "set wepkey failed: %d\n", rval); 2468151207Swpaul keys_set++; 2469124005Swpaul } 2470124005Swpaul } 2471151207Swpaul if (keys_set) { 2472171390Sthompsa DPRINTF(("Setting WEP on\n")); 2473151207Swpaul arg = NDIS_80211_WEPSTAT_ENABLED; 2474151207Swpaul len = sizeof(arg); 2475151207Swpaul rval = ndis_set_info(sc, 2476151207Swpaul OID_802_11_WEP_STATUS, &arg, &len); 2477151207Swpaul if (rval) 2478151207Swpaul device_printf(sc->ndis_dev, 2479151207Swpaul "enable WEP failed: %d\n", rval); 2480178354Ssam if (vap->iv_flags & IEEE80211_F_DROPUNENC) 2481151207Swpaul arg = NDIS_80211_PRIVFILT_8021XWEP; 2482151207Swpaul else 2483151207Swpaul arg = NDIS_80211_PRIVFILT_ACCEPTALL; 2484151207Swpaul 2485151207Swpaul len = sizeof(arg); 2486151207Swpaul ndis_set_info(sc, 2487151207Swpaul OID_802_11_PRIVACY_FILTER, &arg, &len); 2488124409Swpaul } 2489178354Ssam } 2490124005Swpaul 2491151207Swpaul /* Set up WPA. */ 2492178354Ssam if ((vap->iv_flags & IEEE80211_F_WPA) && 2493178354Ssam vap->iv_appie_assocreq != NULL) { 2494178354Ssam struct ieee80211_appie *ie = vap->iv_appie_assocreq; 2495178354Ssam error = ndis_set_wpa(sc, ie->ie_data, ie->ie_len); 2496178354Ssam if (error != 0) 2497151207Swpaul device_printf(sc->ndis_dev, "WPA setup failed\n"); 2498178354Ssam } 2499125068Swpaul 2500129834Swpaul#ifdef notyet 2501129834Swpaul /* Set network type. */ 2502129834Swpaul 2503129834Swpaul arg = 0; 2504129834Swpaul 2505178354Ssam switch (vap->iv_curmode) { 2506129834Swpaul case IEEE80211_MODE_11A: 2507129834Swpaul arg = NDIS_80211_NETTYPE_11OFDM5; 2508129834Swpaul break; 2509129834Swpaul case IEEE80211_MODE_11B: 2510129834Swpaul arg = NDIS_80211_NETTYPE_11DS; 2511129834Swpaul break; 2512129834Swpaul case IEEE80211_MODE_11G: 2513129834Swpaul arg = NDIS_80211_NETTYPE_11OFDM24; 2514129834Swpaul break; 2515129834Swpaul default: 2516129834Swpaul device_printf(sc->ndis_dev, "unknown mode: %d\n", 2517178354Ssam vap->iv_curmode); 2518129834Swpaul } 2519129834Swpaul 2520129834Swpaul if (arg) { 2521171390Sthompsa DPRINTF(("Setting network type to %d\n", arg)); 2522129834Swpaul len = sizeof(arg); 2523129834Swpaul rval = ndis_set_info(sc, OID_802_11_NETWORK_TYPE_IN_USE, 2524129834Swpaul &arg, &len); 2525129834Swpaul if (rval) 2526198786Srpaulo device_printf(sc->ndis_dev, 2527129834Swpaul "set nettype failed: %d\n", rval); 2528129834Swpaul } 2529130051Swpaul#endif 2530129834Swpaul 2531129834Swpaul /* 2532151207Swpaul * If the user selected a specific BSSID, try 2533151207Swpaul * to use that one. This is useful in the case where 2534151207Swpaul * there are several APs in range with the same network 2535151207Swpaul * name. To delete the BSSID, we use the broadcast 2536151207Swpaul * address as the BSSID. 2537151207Swpaul * Note that some drivers seem to allow setting a BSSID 2538151207Swpaul * in ad-hoc mode, which has the effect of forcing the 2539151207Swpaul * NIC to create an ad-hoc cell with a specific BSSID, 2540151207Swpaul * instead of a randomly chosen one. However, the net80211 2541151207Swpaul * code makes the assumtion that the BSSID setting is invalid 2542151207Swpaul * when you're in ad-hoc mode, so we don't allow that here. 2543151207Swpaul */ 2544151207Swpaul 2545151207Swpaul len = IEEE80211_ADDR_LEN; 2546178354Ssam if (vap->iv_flags & IEEE80211_F_DESBSSID && 2547178354Ssam vap->iv_opmode != IEEE80211_M_IBSS) 2548171390Sthompsa bcopy(ni->ni_bssid, bssid, len); 2549151207Swpaul else 2550151207Swpaul bcopy(ifp->if_broadcastaddr, bssid, len); 2551151207Swpaul 2552174270Swkoszek DPRINTF(("Setting BSSID to %6D\n", (uint8_t *)&bssid, ":")); 2553151207Swpaul rval = ndis_set_info(sc, OID_802_11_BSSID, &bssid, &len); 2554151207Swpaul if (rval) 2555151207Swpaul device_printf(sc->ndis_dev, 2556151207Swpaul "setting BSSID failed: %d\n", rval); 2557151207Swpaul 2558128229Swpaul /* Set SSID -- always do this last. */ 2559128229Swpaul 2560171390Sthompsa#ifdef NDIS_DEBUG 2561174157Sthompsa if (ndis_debug > 0) { 2562174157Sthompsa printf("Setting ESSID to "); 2563174157Sthompsa ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); 2564174157Sthompsa printf("\n"); 2565174157Sthompsa } 2566171390Sthompsa#endif 2567171390Sthompsa 2568128229Swpaul len = sizeof(ssid); 2569128229Swpaul bzero((char *)&ssid, len); 2570171390Sthompsa ssid.ns_ssidlen = ni->ni_esslen; 2571128229Swpaul if (ssid.ns_ssidlen == 0) { 2572128229Swpaul ssid.ns_ssidlen = 1; 2573128229Swpaul } else 2574171390Sthompsa bcopy(ni->ni_essid, ssid.ns_ssid, ssid.ns_ssidlen); 2575151207Swpaul 2576128229Swpaul rval = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len); 2577128229Swpaul 2578128229Swpaul if (rval) 2579128229Swpaul device_printf (sc->ndis_dev, "set ssid failed: %d\n", rval); 2580128229Swpaul 2581123695Swpaul return; 2582123695Swpaul} 2583123695Swpaul 2584124409Swpaulstatic int 2585194706Scokanendis_get_bssid_list(sc, bl) 2586194706Scokane struct ndis_softc *sc; 2587194706Scokane ndis_80211_bssid_list_ex **bl; 2588194706Scokane{ 2589194706Scokane int len, error; 2590194706Scokane 2591194706Scokane len = sizeof(uint32_t) + (sizeof(ndis_wlan_bssid_ex) * 16); 2592194706Scokane *bl = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); 2593194706Scokane if (*bl == NULL) 2594194706Scokane return (ENOMEM); 2595194706Scokane 2596194706Scokane error = ndis_get_info(sc, OID_802_11_BSSID_LIST, *bl, &len); 2597194706Scokane if (error == ENOSPC) { 2598194706Scokane free(*bl, M_DEVBUF); 2599194706Scokane *bl = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); 2600194706Scokane if (*bl == NULL) 2601194706Scokane return (ENOMEM); 2602194706Scokane 2603194706Scokane error = ndis_get_info(sc, OID_802_11_BSSID_LIST, *bl, &len); 2604194706Scokane } 2605194706Scokane if (error) { 2606194706Scokane DPRINTF(("%s: failed to read\n", __func__)); 2607194706Scokane free(*bl, M_DEVBUF); 2608194706Scokane return (error); 2609194706Scokane } 2610194706Scokane 2611194706Scokane return (0); 2612194706Scokane} 2613194706Scokane 2614194706Scokanestatic int 2615124409Swpaulndis_get_assoc(sc, assoc) 2616124409Swpaul struct ndis_softc *sc; 2617127349Swpaul ndis_wlan_bssid_ex **assoc; 2618124409Swpaul{ 2619184833Sthompsa struct ifnet *ifp = sc->ifp; 2620184833Sthompsa struct ieee80211com *ic = ifp->if_l2com; 2621184833Sthompsa struct ieee80211vap *vap; 2622184833Sthompsa struct ieee80211_node *ni; 2623124409Swpaul ndis_80211_bssid_list_ex *bl; 2624124409Swpaul ndis_wlan_bssid_ex *bs; 2625124409Swpaul ndis_80211_macaddr bssid; 2626124409Swpaul int i, len, error; 2627124409Swpaul 2628124899Swpaul if (!sc->ndis_link) 2629198786Srpaulo return (ENOENT); 2630124899Swpaul 2631124409Swpaul len = sizeof(bssid); 2632124409Swpaul error = ndis_get_info(sc, OID_802_11_BSSID, &bssid, &len); 2633124409Swpaul if (error) { 2634124899Swpaul device_printf(sc->ndis_dev, "failed to get bssid\n"); 2635198786Srpaulo return (ENOENT); 2636124409Swpaul } 2637152136Swpaul 2638184833Sthompsa vap = TAILQ_FIRST(&ic->ic_vaps); 2639184833Sthompsa ni = vap->iv_bss; 2640184833Sthompsa 2641194706Scokane error = ndis_get_bssid_list(sc, &bl); 2642194706Scokane if (error) 2643124409Swpaul return (error); 2644124409Swpaul 2645124409Swpaul bs = (ndis_wlan_bssid_ex *)&bl->nblx_bssid[0]; 2646124409Swpaul for (i = 0; i < bl->nblx_items; i++) { 2647124409Swpaul if (bcmp(bs->nwbx_macaddr, bssid, sizeof(bssid)) == 0) { 2648127349Swpaul *assoc = malloc(bs->nwbx_len, M_TEMP, M_NOWAIT); 2649127349Swpaul if (*assoc == NULL) { 2650127349Swpaul free(bl, M_TEMP); 2651198786Srpaulo return (ENOMEM); 2652127349Swpaul } 2653127349Swpaul bcopy((char *)bs, (char *)*assoc, bs->nwbx_len); 2654124409Swpaul free(bl, M_TEMP); 2655184833Sthompsa if (ic->ic_opmode == IEEE80211_M_STA) 2656184833Sthompsa ni->ni_associd = 1 | 0xc000; /* fake associd */ 2657198786Srpaulo return (0); 2658184833Sthompsa } 2659124409Swpaul bs = (ndis_wlan_bssid_ex *)((char *)bs + bs->nwbx_len); 2660124409Swpaul } 2661124409Swpaul 2662124409Swpaul free(bl, M_TEMP); 2663198786Srpaulo return (ENOENT); 2664124409Swpaul} 2665124409Swpaul 2666124409Swpaulstatic void 2667123695Swpaulndis_getstate_80211(sc) 2668123695Swpaul struct ndis_softc *sc; 2669123695Swpaul{ 2670123695Swpaul struct ieee80211com *ic; 2671178354Ssam struct ieee80211vap *vap; 2672178354Ssam struct ieee80211_node *ni; 2673127349Swpaul ndis_wlan_bssid_ex *bs; 2674123695Swpaul int rval, len, i = 0; 2675171390Sthompsa int chanflag; 2676123695Swpaul uint32_t arg; 2677123695Swpaul struct ifnet *ifp; 2678123695Swpaul 2679147256Sbrooks ifp = sc->ifp; 2680178354Ssam ic = ifp->if_l2com; 2681178354Ssam vap = TAILQ_FIRST(&ic->ic_vaps); 2682178354Ssam ni = vap->iv_bss; 2683123695Swpaul 2684131750Swpaul if (!NDIS_INITIALIZED(sc)) 2685123695Swpaul return; 2686123695Swpaul 2687171602Sthompsa if ((rval = ndis_get_assoc(sc, &bs)) != 0) 2688125377Swpaul return; 2689124409Swpaul 2690171602Sthompsa /* We're associated, retrieve info on the current bssid. */ 2691171602Sthompsa ic->ic_curmode = ndis_nettype_mode(bs->nwbx_nettype); 2692171602Sthompsa chanflag = ndis_nettype_chan(bs->nwbx_nettype); 2693178354Ssam IEEE80211_ADDR_COPY(ni->ni_bssid, bs->nwbx_macaddr); 2694171602Sthompsa 2695151832Swpaul /* Get SSID from current association info. */ 2696178354Ssam bcopy(bs->nwbx_ssid.ns_ssid, ni->ni_essid, 2697151832Swpaul bs->nwbx_ssid.ns_ssidlen); 2698178354Ssam ni->ni_esslen = bs->nwbx_ssid.ns_ssidlen; 2699123695Swpaul 2700124409Swpaul if (ic->ic_caps & IEEE80211_C_PMGT) { 2701124409Swpaul len = sizeof(arg); 2702124409Swpaul rval = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &len); 2703123695Swpaul 2704124409Swpaul if (rval) 2705124409Swpaul device_printf(sc->ndis_dev, 2706124409Swpaul "get power mode failed: %d\n", rval); 2707124409Swpaul if (arg == NDIS_80211_POWERMODE_CAM) 2708194432Scokane vap->iv_flags &= ~IEEE80211_F_PMGTON; 2709124409Swpaul else 2710194432Scokane vap->iv_flags |= IEEE80211_F_PMGTON; 2711124409Swpaul } 2712124409Swpaul 2713187104Sthompsa /* Get TX power */ 2714189550Ssam if (ic->ic_caps & IEEE80211_C_TXPMGT) { 2715189550Ssam len = sizeof(arg); 2716189550Ssam ndis_get_info(sc, OID_802_11_TX_POWER_LEVEL, &arg, &len); 2717189550Ssam for (i = 0; i < (sizeof(dBm2mW) / sizeof(dBm2mW[0])); i++) 2718187104Sthompsa if (dBm2mW[i] >= arg) 2719187104Sthompsa break; 2720187104Sthompsa ic->ic_txpowlimit = i; 2721187104Sthompsa } 2722187104Sthompsa 2723151832Swpaul /* 2724151832Swpaul * Use the current association information to reflect 2725151832Swpaul * what channel we're on. 2726151832Swpaul */ 2727171390Sthompsa ic->ic_curchan = ieee80211_find_channel(ic, 2728171390Sthompsa bs->nwbx_config.nc_dsconfig / 1000, chanflag); 2729171390Sthompsa if (ic->ic_curchan == NULL) 2730171390Sthompsa ic->ic_curchan = &ic->ic_channels[0]; 2731178354Ssam ni->ni_chan = ic->ic_curchan; 2732171390Sthompsa ic->ic_bsschan = ic->ic_curchan; 2733124409Swpaul 2734151832Swpaul free(bs, M_TEMP); 2735151832Swpaul 2736151832Swpaul /* 2737198786Srpaulo * Determine current authentication mode. 2738151832Swpaul */ 2739123695Swpaul len = sizeof(arg); 2740151832Swpaul rval = ndis_get_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); 2741151832Swpaul if (rval) 2742198786Srpaulo device_printf(sc->ndis_dev, 2743151832Swpaul "get authmode status failed: %d\n", rval); 2744151832Swpaul else { 2745194432Scokane vap->iv_flags &= ~IEEE80211_F_WPA; 2746198786Srpaulo switch (arg) { 2747151832Swpaul case NDIS_80211_AUTHMODE_OPEN: 2748178354Ssam ni->ni_authmode = IEEE80211_AUTH_OPEN; 2749151832Swpaul break; 2750151832Swpaul case NDIS_80211_AUTHMODE_SHARED: 2751178354Ssam ni->ni_authmode = IEEE80211_AUTH_SHARED; 2752151832Swpaul break; 2753151832Swpaul case NDIS_80211_AUTHMODE_AUTO: 2754178354Ssam ni->ni_authmode = IEEE80211_AUTH_AUTO; 2755151832Swpaul break; 2756151832Swpaul case NDIS_80211_AUTHMODE_WPA: 2757151832Swpaul case NDIS_80211_AUTHMODE_WPAPSK: 2758151832Swpaul case NDIS_80211_AUTHMODE_WPANONE: 2759178354Ssam ni->ni_authmode = IEEE80211_AUTH_WPA; 2760194432Scokane vap->iv_flags |= IEEE80211_F_WPA1; 2761151832Swpaul break; 2762151832Swpaul case NDIS_80211_AUTHMODE_WPA2: 2763151832Swpaul case NDIS_80211_AUTHMODE_WPA2PSK: 2764178354Ssam ni->ni_authmode = IEEE80211_AUTH_WPA; 2765194432Scokane vap->iv_flags |= IEEE80211_F_WPA2; 2766151832Swpaul break; 2767151832Swpaul default: 2768178354Ssam ni->ni_authmode = IEEE80211_AUTH_NONE; 2769151832Swpaul break; 2770151832Swpaul } 2771151832Swpaul } 2772151832Swpaul 2773151832Swpaul len = sizeof(arg); 2774123695Swpaul rval = ndis_get_info(sc, OID_802_11_WEP_STATUS, &arg, &len); 2775123695Swpaul 2776123695Swpaul if (rval) 2777198786Srpaulo device_printf(sc->ndis_dev, 2778124060Swpaul "get wep status failed: %d\n", rval); 2779123695Swpaul 2780123695Swpaul if (arg == NDIS_80211_WEPSTAT_ENABLED) 2781194432Scokane vap->iv_flags |= IEEE80211_F_PRIVACY|IEEE80211_F_DROPUNENC; 2782151832Swpaul else 2783194432Scokane vap->iv_flags &= ~(IEEE80211_F_PRIVACY|IEEE80211_F_DROPUNENC); 2784123695Swpaul} 2785123695Swpaul 2786123474Swpaulstatic int 2787123474Swpaulndis_ioctl(ifp, command, data) 2788123474Swpaul struct ifnet *ifp; 2789123474Swpaul u_long command; 2790123474Swpaul caddr_t data; 2791123474Swpaul{ 2792123474Swpaul struct ndis_softc *sc = ifp->if_softc; 2793123474Swpaul struct ifreq *ifr = (struct ifreq *) data; 2794123695Swpaul int i, error = 0; 2795123474Swpaul 2796123474Swpaul /*NDIS_LOCK(sc);*/ 2797123474Swpaul 2798198786Srpaulo switch (command) { 2799123474Swpaul case SIOCSIFFLAGS: 2800123474Swpaul if (ifp->if_flags & IFF_UP) { 2801148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING && 2802123695Swpaul ifp->if_flags & IFF_PROMISC && 2803123695Swpaul !(sc->ndis_if_flags & IFF_PROMISC)) { 2804123695Swpaul sc->ndis_filter |= 2805123695Swpaul NDIS_PACKET_TYPE_PROMISCUOUS; 2806124709Swpaul i = sizeof(sc->ndis_filter); 2807124709Swpaul error = ndis_set_info(sc, 2808123695Swpaul OID_GEN_CURRENT_PACKET_FILTER, 2809123695Swpaul &sc->ndis_filter, &i); 2810148887Srwatson } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && 2811123695Swpaul !(ifp->if_flags & IFF_PROMISC) && 2812123695Swpaul sc->ndis_if_flags & IFF_PROMISC) { 2813123695Swpaul sc->ndis_filter &= 2814123695Swpaul ~NDIS_PACKET_TYPE_PROMISCUOUS; 2815124709Swpaul i = sizeof(sc->ndis_filter); 2816124709Swpaul error = ndis_set_info(sc, 2817123695Swpaul OID_GEN_CURRENT_PACKET_FILTER, 2818123695Swpaul &sc->ndis_filter, &i); 2819123695Swpaul } else 2820123695Swpaul ndis_init(sc); 2821123474Swpaul } else { 2822148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING) 2823123474Swpaul ndis_stop(sc); 2824123474Swpaul } 2825123695Swpaul sc->ndis_if_flags = ifp->if_flags; 2826123474Swpaul error = 0; 2827123474Swpaul break; 2828123474Swpaul case SIOCADDMULTI: 2829123474Swpaul case SIOCDELMULTI: 2830123474Swpaul ndis_setmulti(sc); 2831123474Swpaul error = 0; 2832123474Swpaul break; 2833123474Swpaul case SIOCGIFMEDIA: 2834123474Swpaul case SIOCSIFMEDIA: 2835178704Sthompsa error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 2836123474Swpaul break; 2837124821Swpaul case SIOCSIFCAP: 2838124821Swpaul ifp->if_capenable = ifr->ifr_reqcap; 2839124821Swpaul if (ifp->if_capenable & IFCAP_TXCSUM) 2840124821Swpaul ifp->if_hwassist = sc->ndis_hwassist; 2841124821Swpaul else 2842124821Swpaul ifp->if_hwassist = 0; 2843124821Swpaul ndis_set_offload(sc); 2844124821Swpaul break; 2845178704Sthompsa default: 2846178704Sthompsa error = ether_ioctl(ifp, command, data); 2847178704Sthompsa break; 2848178704Sthompsa } 2849178704Sthompsa 2850178704Sthompsa /*NDIS_UNLOCK(sc);*/ 2851178704Sthompsa 2852178704Sthompsa return(error); 2853178704Sthompsa} 2854178704Sthompsa 2855178704Sthompsastatic int 2856178704Sthompsandis_ioctl_80211(ifp, command, data) 2857178704Sthompsa struct ifnet *ifp; 2858178704Sthompsa u_long command; 2859178704Sthompsa caddr_t data; 2860178704Sthompsa{ 2861178704Sthompsa struct ndis_softc *sc = ifp->if_softc; 2862178704Sthompsa struct ieee80211com *ic = ifp->if_l2com; 2863178704Sthompsa struct ifreq *ifr = (struct ifreq *) data; 2864178704Sthompsa struct ndis_oid_data oid; 2865178704Sthompsa struct ndis_evt evt; 2866178704Sthompsa void *oidbuf; 2867178704Sthompsa int error = 0; 2868178704Sthompsa 2869198786Srpaulo switch (command) { 2870178704Sthompsa case SIOCSIFFLAGS: 2871178704Sthompsa /*NDIS_LOCK(sc);*/ 2872178704Sthompsa if (ifp->if_flags & IFF_UP) { 2873178930Sthompsa if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) 2874178704Sthompsa ndis_init(sc); 2875178704Sthompsa } else { 2876178704Sthompsa if (ifp->if_drv_flags & IFF_DRV_RUNNING) 2877178704Sthompsa ndis_stop(sc); 2878178704Sthompsa } 2879178704Sthompsa sc->ndis_if_flags = ifp->if_flags; 2880178704Sthompsa error = 0; 2881178704Sthompsa /*NDIS_UNLOCK(sc);*/ 2882178704Sthompsa break; 2883151207Swpaul case SIOCGDRVSPEC: 2884164033Srwatson if ((error = priv_check(curthread, PRIV_DRIVER))) 2885151207Swpaul break; 2886151207Swpaul error = copyin(ifr->ifr_data, &oid, sizeof(oid)); 2887151207Swpaul if (error) 2888151207Swpaul break; 2889151207Swpaul oidbuf = malloc(oid.len, M_TEMP, M_NOWAIT|M_ZERO); 2890151207Swpaul if (oidbuf == NULL) { 2891151207Swpaul error = ENOMEM; 2892151207Swpaul break; 2893151207Swpaul } 2894151207Swpaul error = copyin(ifr->ifr_data + sizeof(oid), oidbuf, oid.len); 2895151207Swpaul if (error) { 2896151207Swpaul free(oidbuf, M_TEMP); 2897151207Swpaul break; 2898151207Swpaul } 2899151207Swpaul error = ndis_get_info(sc, oid.oid, oidbuf, &oid.len); 2900151207Swpaul if (error) { 2901151207Swpaul free(oidbuf, M_TEMP); 2902151207Swpaul break; 2903151207Swpaul } 2904151207Swpaul error = copyout(&oid, ifr->ifr_data, sizeof(oid)); 2905151207Swpaul if (error) { 2906151207Swpaul free(oidbuf, M_TEMP); 2907151207Swpaul break; 2908151207Swpaul } 2909151207Swpaul error = copyout(oidbuf, ifr->ifr_data + sizeof(oid), oid.len); 2910151207Swpaul free(oidbuf, M_TEMP); 2911151207Swpaul break; 2912151207Swpaul case SIOCSDRVSPEC: 2913164033Srwatson if ((error = priv_check(curthread, PRIV_DRIVER))) 2914151207Swpaul break; 2915151207Swpaul error = copyin(ifr->ifr_data, &oid, sizeof(oid)); 2916151207Swpaul if (error) 2917151207Swpaul break; 2918151207Swpaul oidbuf = malloc(oid.len, M_TEMP, M_NOWAIT|M_ZERO); 2919151207Swpaul if (oidbuf == NULL) { 2920151207Swpaul error = ENOMEM; 2921151207Swpaul break; 2922151207Swpaul } 2923151207Swpaul error = copyin(ifr->ifr_data + sizeof(oid), oidbuf, oid.len); 2924151207Swpaul if (error) { 2925151207Swpaul free(oidbuf, M_TEMP); 2926151207Swpaul break; 2927151207Swpaul } 2928151207Swpaul error = ndis_set_info(sc, oid.oid, oidbuf, &oid.len); 2929151207Swpaul if (error) { 2930151207Swpaul free(oidbuf, M_TEMP); 2931151207Swpaul break; 2932151207Swpaul } 2933151207Swpaul error = copyout(&oid, ifr->ifr_data, sizeof(oid)); 2934151207Swpaul if (error) { 2935151207Swpaul free(oidbuf, M_TEMP); 2936151207Swpaul break; 2937151207Swpaul } 2938151207Swpaul error = copyout(oidbuf, ifr->ifr_data + sizeof(oid), oid.len); 2939151207Swpaul free(oidbuf, M_TEMP); 2940151207Swpaul break; 2941151207Swpaul case SIOCGPRIVATE_0: 2942164033Srwatson if ((error = priv_check(curthread, PRIV_DRIVER))) 2943151207Swpaul break; 2944151207Swpaul NDIS_LOCK(sc); 2945151207Swpaul if (sc->ndis_evt[sc->ndis_evtcidx].ne_sts == 0) { 2946151207Swpaul error = ENOENT; 2947151207Swpaul NDIS_UNLOCK(sc); 2948151207Swpaul break; 2949151207Swpaul } 2950151207Swpaul error = copyin(ifr->ifr_data, &evt, sizeof(evt)); 2951151217Swpaul if (error) { 2952151217Swpaul NDIS_UNLOCK(sc); 2953151207Swpaul break; 2954151217Swpaul } 2955151207Swpaul if (evt.ne_len < sc->ndis_evt[sc->ndis_evtcidx].ne_len) { 2956151207Swpaul error = ENOSPC; 2957151207Swpaul NDIS_UNLOCK(sc); 2958151207Swpaul break; 2959151207Swpaul } 2960151207Swpaul error = copyout(&sc->ndis_evt[sc->ndis_evtcidx], 2961151207Swpaul ifr->ifr_data, sizeof(uint32_t) * 2); 2962151207Swpaul if (error) { 2963151207Swpaul NDIS_UNLOCK(sc); 2964151207Swpaul break; 2965151207Swpaul } 2966151207Swpaul if (sc->ndis_evt[sc->ndis_evtcidx].ne_len) { 2967151207Swpaul error = copyout(sc->ndis_evt[sc->ndis_evtcidx].ne_buf, 2968151207Swpaul ifr->ifr_data + (sizeof(uint32_t) * 2), 2969151207Swpaul sc->ndis_evt[sc->ndis_evtcidx].ne_len); 2970151216Swpaul if (error) { 2971151216Swpaul NDIS_UNLOCK(sc); 2972151207Swpaul break; 2973151216Swpaul } 2974151207Swpaul free(sc->ndis_evt[sc->ndis_evtcidx].ne_buf, M_TEMP); 2975151207Swpaul sc->ndis_evt[sc->ndis_evtcidx].ne_buf = NULL; 2976151207Swpaul } 2977151207Swpaul sc->ndis_evt[sc->ndis_evtcidx].ne_len = 0; 2978151207Swpaul sc->ndis_evt[sc->ndis_evtcidx].ne_sts = 0; 2979151207Swpaul NDIS_EVTINC(sc->ndis_evtcidx); 2980151207Swpaul NDIS_UNLOCK(sc); 2981151207Swpaul break; 2982178704Sthompsa case SIOCGIFMEDIA: 2983178704Sthompsa error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, command); 2984178704Sthompsa break; 2985178704Sthompsa case SIOCGIFADDR: 2986178354Ssam error = ether_ioctl(ifp, command, data); 2987123474Swpaul break; 2988178704Sthompsa default: 2989178704Sthompsa error = EINVAL; 2990178704Sthompsa break; 2991123474Swpaul } 2992198786Srpaulo return (error); 2993123474Swpaul} 2994123474Swpaul 2995151207Swpaulint 2996178354Ssamndis_del_key(vap, key) 2997178354Ssam struct ieee80211vap *vap; 2998151207Swpaul const struct ieee80211_key *key; 2999151207Swpaul{ 3000145283Swpaul struct ndis_softc *sc; 3001151207Swpaul ndis_80211_key rkey; 3002151207Swpaul int len, error = 0; 3003145283Swpaul 3004178354Ssam sc = vap->iv_ic->ic_ifp->if_softc; 3005145283Swpaul 3006151207Swpaul bzero((char *)&rkey, sizeof(rkey)); 3007151207Swpaul len = sizeof(rkey); 3008145283Swpaul 3009151207Swpaul rkey.nk_len = len; 3010151207Swpaul rkey.nk_keyidx = key->wk_keyix; 3011145283Swpaul 3012178354Ssam bcopy(vap->iv_ifp->if_broadcastaddr, 3013151207Swpaul rkey.nk_bssid, IEEE80211_ADDR_LEN); 3014145283Swpaul 3015151207Swpaul error = ndis_set_info(sc, OID_802_11_REMOVE_KEY, &rkey, &len); 3016145283Swpaul 3017151207Swpaul if (error) 3018198786Srpaulo return (0); 3019151207Swpaul 3020198786Srpaulo return (1); 3021151207Swpaul} 3022151207Swpaul 3023151207Swpaul/* 3024151207Swpaul * In theory this could be called for any key, but we'll 3025151207Swpaul * only use it for WPA TKIP or AES keys. These need to be 3026151207Swpaul * set after initial authentication with the AP. 3027151207Swpaul */ 3028151207Swpaulstatic int 3029178354Ssamndis_add_key(vap, key, mac) 3030178354Ssam struct ieee80211vap *vap; 3031151207Swpaul const struct ieee80211_key *key; 3032151207Swpaul const uint8_t mac[IEEE80211_ADDR_LEN]; 3033151207Swpaul{ 3034151207Swpaul struct ndis_softc *sc; 3035178354Ssam struct ifnet *ifp; 3036151207Swpaul ndis_80211_key rkey; 3037151207Swpaul int len, error = 0; 3038151207Swpaul 3039178354Ssam ifp = vap->iv_ic->ic_ifp; 3040178354Ssam sc = ifp->if_softc; 3041151207Swpaul 3042151207Swpaul switch (key->wk_cipher->ic_cipher) { 3043145283Swpaul case IEEE80211_CIPHER_TKIP: 3044145283Swpaul 3045151207Swpaul len = sizeof(ndis_80211_key); 3046151207Swpaul bzero((char *)&rkey, sizeof(rkey)); 3047145283Swpaul 3048151207Swpaul rkey.nk_len = len; 3049151207Swpaul rkey.nk_keylen = key->wk_keylen; 3050151207Swpaul 3051151207Swpaul if (key->wk_flags & IEEE80211_KEY_SWMIC) 3052151207Swpaul rkey.nk_keylen += 16; 3053151207Swpaul 3054145283Swpaul /* key index - gets weird in NDIS */ 3055145283Swpaul 3056151207Swpaul if (key->wk_keyix != IEEE80211_KEYIX_NONE) 3057151207Swpaul rkey.nk_keyidx = key->wk_keyix; 3058151207Swpaul else 3059151207Swpaul rkey.nk_keyidx = 0; 3060151207Swpaul 3061151207Swpaul if (key->wk_flags & IEEE80211_KEY_XMIT) 3062151207Swpaul rkey.nk_keyidx |= 1 << 31; 3063151207Swpaul 3064151207Swpaul if (key->wk_flags & IEEE80211_KEY_GROUP) { 3065178354Ssam bcopy(ifp->if_broadcastaddr, 3066151207Swpaul rkey.nk_bssid, IEEE80211_ADDR_LEN); 3067145283Swpaul } else { 3068178354Ssam bcopy(vap->iv_bss->ni_bssid, 3069151207Swpaul rkey.nk_bssid, IEEE80211_ADDR_LEN); 3070145283Swpaul /* pairwise key */ 3071151207Swpaul rkey.nk_keyidx |= 1 << 30; 3072145283Swpaul } 3073145283Swpaul 3074145283Swpaul /* need to set bit 29 based on keyrsc */ 3075178354Ssam rkey.nk_keyrsc = key->wk_keyrsc[0]; /* XXX need tid */ 3076145283Swpaul 3077151207Swpaul if (rkey.nk_keyrsc) 3078151207Swpaul rkey.nk_keyidx |= 1 << 29; 3079151207Swpaul 3080151207Swpaul if (key->wk_flags & IEEE80211_KEY_SWMIC) { 3081151207Swpaul bcopy(key->wk_key, rkey.nk_keydata, 16); 3082151207Swpaul bcopy(key->wk_key + 24, rkey.nk_keydata + 16, 8); 3083151207Swpaul bcopy(key->wk_key + 16, rkey.nk_keydata + 24, 8); 3084151207Swpaul } else 3085151207Swpaul bcopy(key->wk_key, rkey.nk_keydata, key->wk_keylen); 3086151207Swpaul 3087151207Swpaul error = ndis_set_info(sc, OID_802_11_ADD_KEY, &rkey, &len); 3088145283Swpaul break; 3089151207Swpaul case IEEE80211_CIPHER_WEP: 3090151207Swpaul error = 0; 3091151207Swpaul break; 3092151207Swpaul /* 3093151207Swpaul * I don't know how to set up keys for the AES 3094151207Swpaul * cipher yet. Is it the same as TKIP? 3095151207Swpaul */ 3096145283Swpaul case IEEE80211_CIPHER_AES_CCM: 3097145283Swpaul default: 3098151207Swpaul error = ENOTTY; 3099151207Swpaul break; 3100145283Swpaul } 3101151207Swpaul 3102151207Swpaul /* We need to return 1 for success, 0 for failure. */ 3103151207Swpaul 3104151207Swpaul if (error) 3105198786Srpaulo return (0); 3106151207Swpaul 3107151207Swpaul return (1); 3108145283Swpaul} 3109145283Swpaul 3110123474Swpaulstatic void 3111151207Swpaulndis_resettask(d, arg) 3112151207Swpaul device_object *d; 3113145895Swpaul void *arg; 3114145895Swpaul{ 3115145895Swpaul struct ndis_softc *sc; 3116145895Swpaul 3117145895Swpaul sc = arg; 3118145895Swpaul ndis_reset_nic(sc); 3119145895Swpaul} 3120145895Swpaul 3121123474Swpaul/* 3122123474Swpaul * Stop the adapter and free any mbufs allocated to the 3123123474Swpaul * RX and TX lists. 3124123474Swpaul */ 3125123474Swpaulstatic void 3126123474Swpaulndis_stop(sc) 3127123474Swpaul struct ndis_softc *sc; 3128123474Swpaul{ 3129123474Swpaul struct ifnet *ifp; 3130151207Swpaul int i; 3131123474Swpaul 3132147256Sbrooks ifp = sc->ifp; 3133178286Scokane callout_drain(&sc->ndis_stat_callout); 3134123474Swpaul 3135124697Swpaul NDIS_LOCK(sc); 3136179498Scokane sc->ndis_tx_timer = 0; 3137124697Swpaul sc->ndis_link = 0; 3138148887Srwatson ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 3139124697Swpaul NDIS_UNLOCK(sc); 3140123474Swpaul 3141189719Sweongyo if (sc->ndis_iftype != PNPBus || 3142189719Sweongyo (sc->ndis_iftype == PNPBus && 3143189719Sweongyo !(sc->ndisusb_status & NDISUSB_STATUS_DETACH) && 3144189719Sweongyo ndisusb_halt != 0)) 3145186507Sweongyo ndis_halt_nic(sc); 3146145898Swpaul 3147151207Swpaul NDIS_LOCK(sc); 3148151207Swpaul for (i = 0; i < NDIS_EVENTS; i++) { 3149190367Sweongyo if (sc->ndis_evt[i].ne_sts && sc->ndis_evt[i].ne_buf != NULL) { 3150151207Swpaul free(sc->ndis_evt[i].ne_buf, M_TEMP); 3151190367Sweongyo sc->ndis_evt[i].ne_buf = NULL; 3152190367Sweongyo } 3153151207Swpaul sc->ndis_evt[i].ne_sts = 0; 3154151207Swpaul sc->ndis_evt[i].ne_len = 0; 3155151207Swpaul } 3156151207Swpaul sc->ndis_evtcidx = 0; 3157151207Swpaul sc->ndis_evtpidx = 0; 3158151207Swpaul NDIS_UNLOCK(sc); 3159123474Swpaul} 3160123474Swpaul 3161123474Swpaul/* 3162123474Swpaul * Stop all chip I/O so that the kernel's probe routines don't 3163123474Swpaul * get confused by errant DMAs when rebooting. 3164123474Swpaul */ 3165126706Swpaulvoid 3166123474Swpaulndis_shutdown(dev) 3167123474Swpaul device_t dev; 3168123474Swpaul{ 3169123474Swpaul struct ndis_softc *sc; 3170125068Swpaul 3171123474Swpaul sc = device_get_softc(dev); 3172146427Swpaul ndis_stop(sc); 3173123474Swpaul} 3174171390Sthompsa 3175171390Sthompsastatic int 3176178354Ssamndis_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 3177171390Sthompsa{ 3178178354Ssam struct ndis_vap *nvp = NDIS_VAP(vap); 3179178354Ssam struct ieee80211com *ic = vap->iv_ic; 3180171390Sthompsa struct ifnet *ifp = ic->ic_ifp; 3181171390Sthompsa struct ndis_softc *sc = ifp->if_softc; 3182178354Ssam enum ieee80211_state ostate; 3183171390Sthompsa 3184171390Sthompsa DPRINTF(("%s: %s -> %s\n", __func__, 3185178354Ssam ieee80211_state_name[vap->iv_state], 3186171390Sthompsa ieee80211_state_name[nstate])); 3187171390Sthompsa 3188178354Ssam ostate = vap->iv_state; 3189178354Ssam vap->iv_state = nstate; 3190171390Sthompsa 3191171390Sthompsa switch (nstate) { 3192171390Sthompsa /* pass on to net80211 */ 3193171390Sthompsa case IEEE80211_S_INIT: 3194171390Sthompsa case IEEE80211_S_SCAN: 3195178354Ssam return nvp->newstate(vap, nstate, arg); 3196171390Sthompsa case IEEE80211_S_ASSOC: 3197178930Sthompsa if (ostate != IEEE80211_S_AUTH) { 3198191746Sthompsa IEEE80211_UNLOCK(ic); 3199191746Sthompsa ndis_auth_and_assoc(sc, vap); 3200191746Sthompsa IEEE80211_LOCK(ic); 3201178930Sthompsa } 3202171390Sthompsa break; 3203171390Sthompsa case IEEE80211_S_AUTH: 3204191746Sthompsa IEEE80211_UNLOCK(ic); 3205191746Sthompsa ndis_auth_and_assoc(sc, vap); 3206191746Sthompsa if (vap->iv_state == IEEE80211_S_AUTH) /* XXX */ 3207191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); 3208191746Sthompsa IEEE80211_LOCK(ic); 3209191746Sthompsa break; 3210171390Sthompsa default: 3211171390Sthompsa break; 3212171390Sthompsa } 3213171390Sthompsa return (0); 3214171390Sthompsa} 3215171390Sthompsa 3216171390Sthompsastatic void 3217191746Sthompsandis_scan(void *arg) 3218171390Sthompsa{ 3219200037Sjhb struct ieee80211vap *vap = arg; 3220171390Sthompsa 3221178354Ssam ieee80211_scan_done(vap); 3222171390Sthompsa} 3223171390Sthompsa 3224171390Sthompsastatic void 3225171390Sthompsandis_scan_results(struct ndis_softc *sc) 3226171390Sthompsa{ 3227178354Ssam struct ieee80211com *ic; 3228178354Ssam struct ieee80211vap *vap; 3229171390Sthompsa ndis_80211_bssid_list_ex *bl; 3230171390Sthompsa ndis_wlan_bssid_ex *wb; 3231171390Sthompsa struct ieee80211_scanparams sp; 3232171390Sthompsa struct ieee80211_frame wh; 3233178930Sthompsa struct ieee80211_channel *saved_chan; 3234171390Sthompsa int i, j; 3235194706Scokane int rssi, noise, freq, chanflag; 3236171390Sthompsa uint8_t ssid[2+IEEE80211_NWID_LEN]; 3237171390Sthompsa uint8_t rates[2+IEEE80211_RATE_MAXSIZE]; 3238171390Sthompsa uint8_t *frm, *efrm; 3239171390Sthompsa 3240178354Ssam ic = sc->ifp->if_l2com; 3241178354Ssam vap = TAILQ_FIRST(&ic->ic_vaps); 3242178930Sthompsa saved_chan = ic->ic_curchan; 3243171390Sthompsa noise = -96; 3244174401Sthompsa 3245194706Scokane if (ndis_get_bssid_list(sc, &bl)) 3246174401Sthompsa return; 3247174401Sthompsa 3248171602Sthompsa DPRINTF(("%s: %d results\n", __func__, bl->nblx_items)); 3249171390Sthompsa wb = &bl->nblx_bssid[0]; 3250171390Sthompsa for (i = 0; i < bl->nblx_items; i++) { 3251171390Sthompsa memset(&sp, 0, sizeof(sp)); 3252171390Sthompsa 3253171390Sthompsa memcpy(wh.i_addr2, wb->nwbx_macaddr, sizeof(wh.i_addr2)); 3254171390Sthompsa memcpy(wh.i_addr3, wb->nwbx_macaddr, sizeof(wh.i_addr3)); 3255171602Sthompsa rssi = 100 * (wb->nwbx_rssi - noise) / (-32 - noise); 3256171390Sthompsa rssi = max(0, min(rssi, 100)); /* limit 0 <= rssi <= 100 */ 3257171390Sthompsa if (wb->nwbx_privacy) 3258171390Sthompsa sp.capinfo |= IEEE80211_CAPINFO_PRIVACY; 3259171390Sthompsa sp.bintval = wb->nwbx_config.nc_beaconperiod; 3260171390Sthompsa switch (wb->nwbx_netinfra) { 3261171390Sthompsa case NDIS_80211_NET_INFRA_IBSS: 3262171390Sthompsa sp.capinfo |= IEEE80211_CAPINFO_IBSS; 3263171390Sthompsa break; 3264171390Sthompsa case NDIS_80211_NET_INFRA_BSS: 3265171390Sthompsa sp.capinfo |= IEEE80211_CAPINFO_ESS; 3266171390Sthompsa break; 3267171390Sthompsa } 3268171390Sthompsa sp.rates = &rates[0]; 3269171390Sthompsa for (j = 0; j < IEEE80211_RATE_MAXSIZE; j++) { 3270171390Sthompsa /* XXX - check units */ 3271171390Sthompsa if (wb->nwbx_supportedrates[j] == 0) 3272171390Sthompsa break; 3273171390Sthompsa rates[2 + j] = 3274171390Sthompsa wb->nwbx_supportedrates[j] & 0x7f; 3275171390Sthompsa } 3276171390Sthompsa rates[1] = j; 3277171390Sthompsa sp.ssid = (uint8_t *)&ssid[0]; 3278171390Sthompsa memcpy(sp.ssid + 2, &wb->nwbx_ssid.ns_ssid, 3279171390Sthompsa wb->nwbx_ssid.ns_ssidlen); 3280171390Sthompsa sp.ssid[1] = wb->nwbx_ssid.ns_ssidlen; 3281171390Sthompsa 3282171602Sthompsa chanflag = ndis_nettype_chan(wb->nwbx_nettype); 3283171602Sthompsa freq = wb->nwbx_config.nc_dsconfig / 1000; 3284178354Ssam sp.chan = sp.bchan = ieee80211_mhz2ieee(freq, chanflag); 3285178930Sthompsa /* Hack ic->ic_curchan to be in sync with the scan result */ 3286178930Sthompsa ic->ic_curchan = ieee80211_find_channel(ic, freq, chanflag); 3287178930Sthompsa if (ic->ic_curchan == NULL) 3288178930Sthompsa ic->ic_curchan = &ic->ic_channels[0]; 3289171390Sthompsa 3290171390Sthompsa /* Process extended info from AP */ 3291171390Sthompsa if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) { 3292171390Sthompsa frm = (uint8_t *)&wb->nwbx_ies; 3293171390Sthompsa efrm = frm + wb->nwbx_ielen; 3294171390Sthompsa if (efrm - frm < 12) 3295171390Sthompsa goto done; 3296200524Srpaulo sp.tstamp = frm; frm += 8; 3297200524Srpaulo sp.bintval = le16toh(*(uint16_t *)frm); frm += 2; 3298200524Srpaulo sp.capinfo = le16toh(*(uint16_t *)frm); frm += 2; 3299200524Srpaulo sp.ies = frm; 3300200524Srpaulo sp.ies_len = efrm - frm; 3301171390Sthompsa } 3302171390Sthompsadone: 3303171602Sthompsa DPRINTF(("scan: bssid %s chan %dMHz (%d/%d) rssi %d\n", 3304171602Sthompsa ether_sprintf(wb->nwbx_macaddr), freq, sp.bchan, chanflag, 3305171602Sthompsa rssi)); 3306192497Ssam ieee80211_add_scan(vap, &sp, &wh, 0, rssi, noise); 3307171390Sthompsa wb = (ndis_wlan_bssid_ex *)((char *)wb + wb->nwbx_len); 3308171390Sthompsa } 3309171602Sthompsa free(bl, M_DEVBUF); 3310178930Sthompsa /* Restore the channel after messing with it */ 3311178930Sthompsa ic->ic_curchan = saved_chan; 3312171390Sthompsa} 3313171390Sthompsa 3314171390Sthompsastatic void 3315171390Sthompsandis_scan_start(struct ieee80211com *ic) 3316171390Sthompsa{ 3317171390Sthompsa struct ifnet *ifp = ic->ic_ifp; 3318171390Sthompsa struct ndis_softc *sc = ifp->if_softc; 3319191746Sthompsa struct ieee80211vap *vap; 3320191746Sthompsa struct ieee80211_scan_state *ss; 3321191746Sthompsa ndis_80211_ssid ssid; 3322191746Sthompsa int error, len; 3323171390Sthompsa 3324191746Sthompsa ss = ic->ic_scan; 3325191746Sthompsa vap = TAILQ_FIRST(&ic->ic_vaps); 3326191746Sthompsa 3327191746Sthompsa if (!NDIS_INITIALIZED(sc)) { 3328191746Sthompsa DPRINTF(("%s: scan aborted\n", __func__)); 3329191746Sthompsa ieee80211_cancel_scan(vap); 3330191746Sthompsa return; 3331191746Sthompsa } 3332191746Sthompsa 3333191746Sthompsa len = sizeof(ssid); 3334191746Sthompsa bzero((char *)&ssid, len); 3335191746Sthompsa if (ss->ss_nssid == 0) 3336191746Sthompsa ssid.ns_ssidlen = 1; 3337191746Sthompsa else { 3338191746Sthompsa /* Perform a directed scan */ 3339191746Sthompsa ssid.ns_ssidlen = ss->ss_ssid[0].len; 3340191746Sthompsa bcopy(ss->ss_ssid[0].ssid, ssid.ns_ssid, ssid.ns_ssidlen); 3341191746Sthompsa } 3342191746Sthompsa 3343191746Sthompsa error = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len); 3344191746Sthompsa if (error) 3345191746Sthompsa DPRINTF(("%s: set ESSID failed\n", __func__)); 3346191746Sthompsa 3347191746Sthompsa len = 0; 3348198786Srpaulo error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN, NULL, &len); 3349191746Sthompsa if (error) { 3350191746Sthompsa DPRINTF(("%s: scan command failed\n", __func__)); 3351191746Sthompsa ieee80211_cancel_scan(vap); 3352191746Sthompsa return; 3353191746Sthompsa } 3354191746Sthompsa /* Set a timer to collect the results */ 3355200037Sjhb callout_reset(&sc->ndis_scan_callout, hz * 3, ndis_scan, vap); 3356171390Sthompsa} 3357171390Sthompsa 3358171390Sthompsastatic void 3359171390Sthompsandis_set_channel(struct ieee80211com *ic) 3360171390Sthompsa{ 3361171390Sthompsa /* ignore */ 3362171390Sthompsa} 3363171390Sthompsa 3364171390Sthompsastatic void 3365178354Ssamndis_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 3366171390Sthompsa{ 3367171390Sthompsa /* ignore */ 3368171390Sthompsa} 3369171390Sthompsa 3370171390Sthompsastatic void 3371178354Ssamndis_scan_mindwell(struct ieee80211_scan_state *ss) 3372171390Sthompsa{ 3373171390Sthompsa /* NB: don't try to abort scan; wait for firmware to finish */ 3374171390Sthompsa} 3375171390Sthompsa 3376171390Sthompsastatic void 3377171390Sthompsandis_scan_end(struct ieee80211com *ic) 3378171390Sthompsa{ 3379200037Sjhb struct ndis_softc *sc = ic->ic_ifp->if_softc; 3380200037Sjhb 3381200037Sjhb ndis_scan_results(sc); 3382171390Sthompsa} 3383