Sync usage with man page.
[netbsd-mini2440.git] / sys / dev / if_ndis / if_ndis.c
blobf66365ef85e275682c782a4a202b2cc55c333b08
1 /*-
2 * Copyright (c) 2003
3 * Bill Paul <wpaul@windriver.com>. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30 * THE POSSIBILITY OF SUCH DAMAGE.
33 #include <sys/cdefs.h>
34 #ifdef __FreeBSD__
35 __FBSDID("$FreeBSD: src/sys/dev/if_ndis/if_ndis.c,v 1.69.2.6 2005/03/31 04:24:36 wpaul Exp $");
36 #endif
37 #ifdef __NetBSD__
38 __KERNEL_RCSID(0, "$NetBSD: if_ndis.c,v 1.28 2009/08/02 20:22:34 dsl Exp $");
39 #endif
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/sockio.h>
45 #include <sys/mbuf.h>
46 #include <sys/malloc.h>
47 #include <sys/kernel.h>
48 #include <sys/socket.h>
49 #include <sys/queue.h>
51 #include <sys/device.h>
52 #include <sys/kauth.h>
54 #include <sys/module.h>
56 #include <sys/proc.h>
57 #include <sys/sysctl.h>
59 #include <net/if.h>
60 #include <net/if_arp.h>
62 #include <net/if_ether.h>
64 #include <net/if_dl.h>
65 #include <net/if_media.h>
66 #include <net/route.h>
68 #include <net/bpf.h>
71 #include <sys/bus.h>
74 #include <net80211/ieee80211_var.h>
75 #include <net80211/ieee80211_ioctl.h>
77 #include <dev/ic/wi_ieee.h>
79 #include <dev/pci/pcireg.h>
80 #include <dev/pci/pcivar.h>
81 #include <dev/pci/pcidevs.h>
83 #include <compat/ndis/pe_var.h>
84 #include <compat/ndis/resource_var.h>
85 #include <compat/ndis/ntoskrnl_var.h>
86 #include <compat/ndis/hal_var.h>
87 #include <compat/ndis/ndis_var.h>
88 #include <compat/ndis/cfg_var.h>
89 #include <compat/ndis/nbcompat.h>
90 #include <dev/if_ndis/if_ndisvar.h>
92 #define NDIS_IMAGE
93 #define NDIS_REGVALS
95 #include "ndis_driver_data.h"
97 void ndis_attach(void *);
98 int ndis_detach(device_t, int);
99 int ndis_suspend(device_t);
100 int ndis_resume(device_t);
101 void ndis_shutdown(device_t);
104 /* I moved these to if_ndisvar.h */
106 static __stdcall void ndis_txeof (ndis_handle,
107 ndis_packet *, ndis_status);
108 static __stdcall void ndis_rxeof (ndis_handle,
109 ndis_packet **, uint32_t);
110 static __stdcall void ndis_linksts (ndis_handle,
111 ndis_status, void *, uint32_t);
112 static __stdcall void ndis_linksts_done (ndis_handle);
115 /* We need to wrap these functions for amd64. */
117 static funcptr ndis_txeof_wrap;
118 static funcptr ndis_rxeof_wrap;
119 static funcptr ndis_linksts_wrap;
120 static funcptr ndis_linksts_done_wrap;
122 int ndis_intr(void *);
123 static void ndis_tick (void *);
124 static void ndis_ticktask (void *);
125 static void ndis_start (struct ifnet *);
126 static void ndis_starttask (void *);
127 static int ndis_ioctl (struct ifnet *, u_long, void *);
128 static int ndis_wi_ioctl_get (struct ifnet *, u_long, void *);
129 static int ndis_wi_ioctl_set (struct ifnet *, u_long, void *);
130 static int ndis_init (struct ifnet *);
131 static void ndis_stop (struct ndis_softc *);
132 static void ndis_watchdog (struct ifnet *);
133 static int ndis_ifmedia_upd (struct ifnet *);
134 static void ndis_ifmedia_sts (struct ifnet *, struct ifmediareq *);
135 static int ndis_get_assoc (struct ndis_softc *, ndis_wlan_bssid_ex **);
136 static int ndis_probe_offload (struct ndis_softc *);
137 static int ndis_set_offload (struct ndis_softc *);
138 static void ndis_getstate_80211 (struct ndis_softc *);
139 static void ndis_setstate_80211 (struct ndis_softc *);
140 static void ndis_media_status (struct ifnet *, struct ifmediareq *);
142 static void ndis_setmulti (struct ndis_softc *);
143 static void ndis_map_sclist (void *, bus_dma_segment_t *,
144 int, bus_size_t, int);
146 #ifdef _MODULE
148 static int ndisdrv_loaded = 0;
149 int ndisdrv_modevent(module_t, int);
151 MODULE(MODULE_CLASS_DRIVER, ndisdrv_modevent, NULL);
153 * This routine should call windrv_load() once for each driver
154 * image. This will do the relocation and dynalinking for the
155 * image, and create a Windows driver object which will be
156 * saved in our driver database.
159 ndisdrv_modevent(module_t mod, int cmd)
161 int error = 0;
163 #ifdef NDIS_DBG
164 printf("in ndisdrv_modevent\n");
165 #endif
166 switch (cmd) {
167 case MODULE_CMD_INIT:
168 ndisdrv_loaded++;
169 if (ndisdrv_loaded > 1)
170 break;
171 error = windrv_load(mod, (vm_offset_t)drv_data, 0);
172 windrv_wrap((funcptr)ndis_rxeof, &ndis_rxeof_wrap);
173 windrv_wrap((funcptr)ndis_txeof, &ndis_txeof_wrap);
174 windrv_wrap((funcptr)ndis_linksts, &ndis_linksts_wrap);
175 windrv_wrap((funcptr)ndis_linksts_done,
176 &ndis_linksts_done_wrap);
177 break;
178 case MODULE_CMD_FINI:
179 ndisdrv_loaded--;
180 if (ndisdrv_loaded > 0)
181 break;
182 windrv_unload(mod, (vm_offset_t)drv_data, 0);
183 windrv_unwrap(ndis_rxeof_wrap);
184 windrv_unwrap(ndis_txeof_wrap);
185 windrv_unwrap(ndis_linksts_wrap);
186 windrv_unwrap(ndis_linksts_done_wrap);
187 break;
188 case MODULE_CMD_STAT:
189 error = ENOTTY;
190 break;
191 default:
192 error = EINVAL;
193 break;
196 return error;
199 int if_ndis_lkmentry(struct lkm_table *lkmtp, int cmd, int ver);
201 CFDRIVER_DECL(ndis, DV_DULL, NULL);
202 extern struct cfattach ndis_ca;
204 static int pciloc[] = { -1, -1 }; /* device, function */
205 static struct cfparent pciparent = {
206 "pci", "pci", DVUNIT_ANY
208 static struct cfdata ndis_cfdata[] = {
209 {"ndis", "ndis", 0, FSTATE_STAR, pciloc, 0, &pciparent, 0},
210 { 0 }
213 static struct cfdriver *ndis_cfdrivers[] = {
214 &ndis_cd,
215 NULL
217 static struct cfattach *ndis_cfattachs[] = {
218 &ndis_ca,
219 NULL
221 static const struct cfattachlkminit ndis_cfattachinit[] = {
222 { "ndis", ndis_cfattachs },
223 { NULL }
226 MOD_DRV("ndis", ndis_cfdrivers, ndis_cfattachinit,
227 ndis_cfdata);
230 if_ndis_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
232 DISPATCH(lkmtp, cmd, ver, lkm_nofunc, lkm_nofunc, lkm_nofunc);
235 #endif /* _MODULE */
238 * Program the 64-bit multicast hash filter.
240 static void
241 ndis_setmulti(struct ndis_softc *sc)
243 struct ifnet *ifp;
244 struct ether_multi *ifma;
245 int len, mclistsz, error;
246 uint8_t *mclist;
248 ifp = &sc->arpcom.ac_if;
250 if (!NDIS_INITIALIZED(sc))
251 return;
253 if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
254 sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
255 len = sizeof(sc->ndis_filter);
256 error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER,
257 &sc->ndis_filter, &len);
258 if (error) {
259 aprint_error_dev(sc->ndis_dev, "set filter failed: %d\n",
260 error);
262 return;
265 if (LIST_EMPTY(&sc->arpcom.ec_multiaddrs))
266 return;
268 len = sizeof(mclistsz);
269 ndis_get_info(sc, OID_802_3_MAXIMUM_LIST_SIZE, &mclistsz, &len);
271 mclist = malloc(ETHER_ADDR_LEN * mclistsz, M_TEMP, M_NOWAIT|M_ZERO);
273 if (mclist == NULL) {
274 sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
275 goto out;
278 sc->ndis_filter |= NDIS_PACKET_TYPE_MULTICAST;
280 len = 0;
281 LIST_FOREACH(ifma, &sc->arpcom.ec_multiaddrs, enm_list) {
283 *****************************************************************************
284 * TODO: The NetBSD ether_multi structure (sys/net/if_ether.h) defines a range
285 * of addresses TODO: (enm_addrlo to enm_addrhi), but FreeBSD's ifmultiaddr
286 * structure (in sys/net/if_var.h) defines only a single address. Do we need
287 * to add every address in the range to the list? Seems like it to me.
288 * But for right now I'm assuming there is only a single address.
289 *****************************************************************************
291 memcpy(mclist + (ETHER_ADDR_LEN * len), ifma->enm_addrlo,
292 ETHER_ADDR_LEN);
293 len++;
294 if (len > mclistsz) {
295 sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
296 sc->ndis_filter &= ~NDIS_PACKET_TYPE_MULTICAST;
297 goto out;
301 len = len * ETHER_ADDR_LEN;
302 error = ndis_set_info(sc, OID_802_3_MULTICAST_LIST, mclist, &len);
303 if (error) {
304 aprint_error_dev(sc->ndis_dev, "set mclist failed: %d\n",
305 error);
306 sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
307 sc->ndis_filter &= ~NDIS_PACKET_TYPE_MULTICAST;
310 out:
311 free(mclist, M_TEMP);
313 len = sizeof(sc->ndis_filter);
314 error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER,
315 &sc->ndis_filter, &len);
316 if (error) {
317 aprint_error_dev(sc->ndis_dev, "set filter failed: %d\n",
318 error);
321 return;
324 static int
325 ndis_set_offload(struct ndis_softc *sc)
327 ndis_task_offload *nto;
328 ndis_task_offload_hdr *ntoh;
329 ndis_task_tcpip_csum *nttc;
330 struct ifnet *ifp;
331 int len, error;
333 ifp = &sc->arpcom.ac_if;
335 if (!NDIS_INITIALIZED(sc))
336 return(EINVAL);
338 /* See if there's anything to set. */
340 error = ndis_probe_offload(sc);
341 if (error)
342 return(error);
344 if (sc->ndis_hwassist == 0 && ifp->if_capabilities == 0)
345 return(0);
347 len = sizeof(ndis_task_offload_hdr) + sizeof(ndis_task_offload) +
348 sizeof(ndis_task_tcpip_csum);
350 ntoh = malloc(len, M_TEMP, M_NOWAIT|M_ZERO);
352 if (ntoh == NULL)
353 return(ENOMEM);
355 ntoh->ntoh_vers = NDIS_TASK_OFFLOAD_VERSION;
356 ntoh->ntoh_len = sizeof(ndis_task_offload_hdr);
357 ntoh->ntoh_offset_firsttask = sizeof(ndis_task_offload_hdr);
358 ntoh->ntoh_encapfmt.nef_encaphdrlen = sizeof(struct ether_header);
359 ntoh->ntoh_encapfmt.nef_encap = NDIS_ENCAP_IEEE802_3;
360 ntoh->ntoh_encapfmt.nef_flags = NDIS_ENCAPFLAG_FIXEDHDRLEN;
362 nto = (ndis_task_offload *)((char *)ntoh +
363 ntoh->ntoh_offset_firsttask);
365 nto->nto_vers = NDIS_TASK_OFFLOAD_VERSION;
366 nto->nto_len = sizeof(ndis_task_offload);
367 nto->nto_task = NDIS_TASK_TCPIP_CSUM;
368 nto->nto_offset_nexttask = 0;
369 nto->nto_taskbuflen = sizeof(ndis_task_tcpip_csum);
371 nttc = (ndis_task_tcpip_csum *)nto->nto_taskbuf;
373 if (ifp->if_capenable & IFCAP_TXCSUM)
374 nttc->nttc_v4tx = sc->ndis_v4tx;
376 if (ifp->if_capenable & IFCAP_RXCSUM)
377 nttc->nttc_v4rx = sc->ndis_v4rx;
379 error = ndis_set_info(sc, OID_TCP_TASK_OFFLOAD, ntoh, &len);
380 free(ntoh, M_TEMP);
382 return(error);
385 static int
386 ndis_probe_offload(struct ndis_softc *sc)
388 ndis_task_offload *nto;
389 ndis_task_offload_hdr *ntoh;
390 ndis_task_tcpip_csum *nttc = NULL;
391 struct ifnet *ifp;
392 int len, error, dummy;
394 ifp = &sc->arpcom.ac_if;
396 len = sizeof(dummy);
397 error = ndis_get_info(sc, OID_TCP_TASK_OFFLOAD, &dummy, &len);
399 if (error != ENOSPC)
400 return(error);
402 ntoh = malloc(len, M_TEMP, M_NOWAIT|M_ZERO);
404 if (ntoh == NULL)
405 return(ENOMEM);
407 ntoh->ntoh_vers = NDIS_TASK_OFFLOAD_VERSION;
408 ntoh->ntoh_len = sizeof(ndis_task_offload_hdr);
409 ntoh->ntoh_encapfmt.nef_encaphdrlen = sizeof(struct ether_header);
410 ntoh->ntoh_encapfmt.nef_encap = NDIS_ENCAP_IEEE802_3;
411 ntoh->ntoh_encapfmt.nef_flags = NDIS_ENCAPFLAG_FIXEDHDRLEN;
413 error = ndis_get_info(sc, OID_TCP_TASK_OFFLOAD, ntoh, &len);
415 if (error) {
416 free(ntoh, M_TEMP);
417 return(error);
420 if (ntoh->ntoh_vers != NDIS_TASK_OFFLOAD_VERSION) {
421 free(ntoh, M_TEMP);
422 return(EINVAL);
425 nto = (ndis_task_offload *)((char *)ntoh +
426 ntoh->ntoh_offset_firsttask);
428 while (1) {
429 switch (nto->nto_task) {
430 case NDIS_TASK_TCPIP_CSUM:
431 nttc = (ndis_task_tcpip_csum *)nto->nto_taskbuf;
432 break;
433 /* Don't handle these yet. */
434 case NDIS_TASK_IPSEC:
435 case NDIS_TASK_TCP_LARGESEND:
436 default:
437 break;
439 if (nto->nto_offset_nexttask == 0)
440 break;
441 nto = (ndis_task_offload *)((char *)nto +
442 nto->nto_offset_nexttask);
445 if (nttc == NULL) {
446 free(ntoh, M_TEMP);
447 return(ENOENT);
450 sc->ndis_v4tx = nttc->nttc_v4tx;
451 sc->ndis_v4rx = nttc->nttc_v4rx;
453 if (nttc->nttc_v4tx & NDIS_TCPSUM_FLAGS_IP_CSUM)
454 sc->ndis_hwassist |= CSUM_IP;
455 if (nttc->nttc_v4tx & NDIS_TCPSUM_FLAGS_TCP_CSUM)
456 sc->ndis_hwassist |= CSUM_TCP;
457 if (nttc->nttc_v4tx & NDIS_TCPSUM_FLAGS_UDP_CSUM)
458 sc->ndis_hwassist |= CSUM_UDP;
459 if (sc->ndis_hwassist)
460 ifp->if_capabilities |= IFCAP_TXCSUM;
462 if (nttc->nttc_v4rx & NDIS_TCPSUM_FLAGS_IP_CSUM)
463 ifp->if_capabilities |= IFCAP_RXCSUM;
464 if (nttc->nttc_v4rx & NDIS_TCPSUM_FLAGS_TCP_CSUM)
465 ifp->if_capabilities |= IFCAP_RXCSUM;
466 if (nttc->nttc_v4rx & NDIS_TCPSUM_FLAGS_UDP_CSUM)
467 ifp->if_capabilities |= IFCAP_RXCSUM;
469 free(ntoh, M_TEMP);
470 return(0);
474 * Attach the interface. Allocate softc structures, do ifmedia
475 * setup and ethernet/BPF attach.
477 void
478 ndis_attach(dev)
479 void *dev;
481 u_char eaddr[ETHER_ADDR_LEN];
482 struct ndis_softc *sc;
483 driver_object *drv;
484 driver_object *pdrv;
485 device_object *pdo;
486 struct ifnet *ifp = NULL;
487 void *img;
488 int error = 0, len;
489 int j;
491 #ifdef NDIS_DBG
492 printf("In ndis_attach()\n");
493 #endif
495 sc = device_get_softc(dev);
497 /* start out at dispatch level */
498 win_irql = DISPATCH_LEVEL;
500 simple_lock_init(&sc->ndis_mtx);
503 * Hook interrupt early, since calling the driver's
504 * init routine may trigger an interrupt. Note that
505 * we don't need to do any explicit interrupt setup
506 * for USB.
509 * For NetBSD, the interrupt is set up in the bus-dependent
510 * code. For PCI it's done in ndis_attach_pci()
514 * TODO: remove this #ifdef once if_ndis_pcmcia.c compiles
517 sc->ndis_regvals = ndis_regvals;
520 /* Create sysctl registry nodes */
521 ndis_create_sysctls(sc);
523 /* Find the PDO for this device instance. */
524 if (sc->ndis_iftype == PCIBus)
525 pdrv = windrv_lookup(0, "PCI Bus");
526 else if (sc->ndis_iftype == PCMCIABus)
527 pdrv = windrv_lookup(0, "PCCARD Bus");
528 else
529 pdrv = windrv_lookup(0, "USB Bus");
530 /* here dev is actuially just a pointer to the softc */
531 pdo = windrv_find_pdo(pdrv, sc->ndis_dev->dv_parent);
535 * Create a new functional device object for this
536 * device. This is what creates the miniport block
537 * for this device instance.
540 img = drv_data;
541 drv = windrv_lookup((vm_offset_t)img, NULL);
543 * Stash a pointer to the softc in the Windows device_object, since
544 * we can't get it from the NetBSD device structure.
546 pdo->pdo_sc = sc;
547 pdo->fdo_sc = sc;
549 if (NdisAddDevice(drv, pdo) != STATUS_SUCCESS) {
550 aprint_error_dev(sc->ndis_dev, "failed to create FDO!\n");
551 error = ENXIO;
552 goto fail;
555 /* Tell the user what version of the API the driver is using. */
556 aprint_normal_dev(sc->ndis_dev, "NDIS API version: %d.%d\n",
557 sc->ndis_chars->nmc_version_major,
558 sc->ndis_chars->nmc_version_minor);
561 * For NetBSD so far we do the resource conversion directly in
562 * ndis_attach_pci()
565 /* Install our RX and TX interrupt handlers. */
566 sc->ndis_block->nmb_senddone_func = ndis_txeof_wrap;
567 sc->ndis_block->nmb_pktind_func = ndis_rxeof_wrap;
569 /* Set up the resource list in the block */
570 sc->ndis_block->nmb_rlist = sc->ndis_rl;
571 /* sc->ndis_block->nmb_rlist = &sc->ndis_rl; */
573 /* TODO: Free this memory! */
574 sc->arpcom.ec_if.if_sadl =
575 malloc(sizeof(struct sockaddr_dl), M_DEVBUF, M_NOWAIT|M_ZERO);
577 /* Call driver's init routine. */
578 if (ndis_init_nic(sc)) {
579 aprint_error_dev(sc->ndis_dev, "init handler failed\n");
580 error = ENXIO;
581 goto fail;
585 * Get station address from the driver.
587 len = sizeof(eaddr);
588 ndis_get_info(sc, OID_802_3_CURRENT_ADDRESS, &eaddr, &len);
591 * Figure out if we're allowed to use multipacket sends
592 * with this driver, and if so, how many.
595 if (sc->ndis_chars->nmc_sendsingle_func &&
596 sc->ndis_chars->nmc_sendmulti_func == NULL) {
597 sc->ndis_maxpkts = 1;
598 } else {
599 len = sizeof(sc->ndis_maxpkts);
600 ndis_get_info(sc, OID_GEN_MAXIMUM_SEND_PACKETS,
601 &sc->ndis_maxpkts, &len);
604 sc->ndis_txarray = malloc(sizeof(ndis_packet *) *
605 sc->ndis_maxpkts, M_DEVBUF, M_NOWAIT|M_ZERO);
607 /* Allocate a pool of ndis_packets for TX encapsulation. */
609 NdisAllocatePacketPool(&j, &sc->ndis_txpool,
610 sc->ndis_maxpkts, PROTOCOL_RESERVED_SIZE_IN_PACKET);
612 if (j != NDIS_STATUS_SUCCESS) {
613 sc->ndis_txpool = NULL;
614 aprint_error_dev(sc->ndis_dev, "failed to allocate TX packet pool");
615 error = ENOMEM;
616 goto fail;
619 sc->ndis_txpending = sc->ndis_maxpkts;
621 sc->ndis_oidcnt = 0;
622 /* Get supported oid list. */
623 ndis_get_supported_oids(sc, &sc->ndis_oids, &sc->ndis_oidcnt);
625 /* If the NDIS module requested scatter/gather, init maps. */
626 if (sc->ndis_sc)
627 ndis_init_dma(sc);
630 * See if the OID_802_11_CONFIGURATION OID is
631 * supported by this driver. If it is, then this an 802.11
632 * wireless driver, and we should set up media for wireless.
634 for (j = 0; j < sc->ndis_oidcnt; j++) {
635 if (sc->ndis_oids[j] == OID_802_11_CONFIGURATION) {
636 sc->ndis_80211++;
637 break;
641 /* Check for task offload support. */
642 ndis_probe_offload(sc);
644 ifp = &sc->arpcom.ac_if;
645 ifp->if_softc = sc;
647 sc->ic.ic_ifp = ifp;
649 strlcpy(ifp->if_xname, device_xname(sc->ndis_dev), IFNAMSIZ);
650 ifp->if_mtu = ETHERMTU;
651 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
652 ifp->if_ioctl = ndis_ioctl;
653 ifp->if_start = ndis_start;
654 ifp->if_watchdog = ndis_watchdog;
655 ifp->if_init = ndis_init;
656 ifp->if_baudrate = 10000000;
657 IFQ_SET_MAXLEN(&ifp->if_snd, 50);
658 IFQ_SET_READY(&ifp->if_snd);
659 ifp->if_capenable = ifp->if_capabilities;
661 * TODO: I don't think NetBSD has this field describing "HW offload
662 * capabilities" as found in FreeBSD's
663 * if_data structure, but maybe there is something else that
664 * needs to be done here for NetBSD
667 /* Do media setup */
668 if (sc->ndis_80211) {
669 struct ieee80211com *ic = &sc->ic;
670 ndis_80211_rates_ex rates;
671 struct ndis_80211_nettype_list *ntl;
672 uint32_t arg;
673 int r;
675 ic->ic_phytype = IEEE80211_T_DS;
676 ic->ic_opmode = IEEE80211_M_STA;
677 ic->ic_caps = IEEE80211_C_IBSS;
678 ic->ic_state = IEEE80211_S_ASSOC;
679 ic->ic_modecaps = (1<<IEEE80211_MODE_AUTO);
680 len = 0;
681 r = ndis_get_info(sc, OID_802_11_NETWORK_TYPES_SUPPORTED,
682 NULL, &len);
683 if (r != ENOSPC)
684 goto nonettypes;
685 ntl = malloc(len, M_DEVBUF, M_WAITOK|M_ZERO);
686 r = ndis_get_info(sc, OID_802_11_NETWORK_TYPES_SUPPORTED,
687 ntl, &len);
688 if (r != 0) {
689 free(ntl, M_DEVBUF);
690 goto nonettypes;
693 for (j = 0; j < ntl->ntl_items; j++) {
694 switch (ntl->ntl_type[j]) {
695 case NDIS_80211_NETTYPE_11FH:
696 case NDIS_80211_NETTYPE_11DS:
697 ic->ic_modecaps |= (1<<IEEE80211_MODE_11B);
698 break;
699 case NDIS_80211_NETTYPE_11OFDM5:
700 ic->ic_modecaps |= (1<<IEEE80211_MODE_11A);
701 break;
702 case NDIS_80211_NETTYPE_11OFDM24:
703 ic->ic_modecaps |= (1<<IEEE80211_MODE_11G);
704 break;
705 default:
706 break;
709 free(ntl, M_DEVBUF);
710 nonettypes:
711 len = sizeof(rates);
712 memset((char *)&rates, 0, len);
713 r = ndis_get_info(sc, OID_802_11_SUPPORTED_RATES,
714 (void *)rates, &len);
715 if (r)
716 aprint_error_dev(sc->ndis_dev, "get rates failed: 0x%x\n", r);
718 * Since the supported rates only up to 8 can be supported,
719 * if this is not 802.11b we're just going to be faking it
720 * all up to heck.
723 #define TESTSETRATE(x, y) \
724 do { \
725 int i; \
726 for (i = 0; i < ic->ic_sup_rates[x].rs_nrates; i++) { \
727 if (ic->ic_sup_rates[x].rs_rates[i] == (y)) \
728 break; \
730 if (i == ic->ic_sup_rates[x].rs_nrates) { \
731 ic->ic_sup_rates[x].rs_rates[i] = (y); \
732 ic->ic_sup_rates[x].rs_nrates++; \
734 } while (0)
736 #define SETRATE(x, y) \
737 ic->ic_sup_rates[x].rs_rates[ic->ic_sup_rates[x].rs_nrates] = (y)
738 #define INCRATE(x) \
739 ic->ic_sup_rates[x].rs_nrates++
741 ic->ic_curmode = IEEE80211_MODE_AUTO;
742 if (ic->ic_modecaps & (1<<IEEE80211_MODE_11A))
743 ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates = 0;
744 if (ic->ic_modecaps & (1<<IEEE80211_MODE_11B))
745 ic->ic_sup_rates[IEEE80211_MODE_11B].rs_nrates = 0;
746 if (ic->ic_modecaps & (1<<IEEE80211_MODE_11G))
747 ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates = 0;
748 for (j = 0; j < len; j++) {
749 switch (rates[j] & IEEE80211_RATE_VAL) {
750 case 2:
751 case 4:
752 case 11:
753 case 10:
754 case 22:
755 if (!(ic->ic_modecaps &
756 (1<<IEEE80211_MODE_11B))) {
757 /* Lazy-init 802.11b. */
758 ic->ic_modecaps |=
759 (1<<IEEE80211_MODE_11B);
760 ic->ic_sup_rates[IEEE80211_MODE_11B].
761 rs_nrates = 0;
763 SETRATE(IEEE80211_MODE_11B, rates[j]);
764 INCRATE(IEEE80211_MODE_11B);
765 break;
766 default:
767 if (ic->ic_modecaps & (1<<IEEE80211_MODE_11A)) {
768 SETRATE(IEEE80211_MODE_11A, rates[j]);
769 INCRATE(IEEE80211_MODE_11A);
771 if (ic->ic_modecaps & (1<<IEEE80211_MODE_11G)) {
772 SETRATE(IEEE80211_MODE_11G, rates[j]);
773 INCRATE(IEEE80211_MODE_11G);
775 break;
780 * If the hardware supports 802.11g, it most
781 * likely supports 802.11b and all of the
782 * 802.11b and 802.11g speeds, so maybe we can
783 * just cheat here. Just how in the heck do
784 * we detect turbo modes, though?
786 if (ic->ic_modecaps & (1<<IEEE80211_MODE_11B)) {
787 TESTSETRATE(IEEE80211_MODE_11B,
788 IEEE80211_RATE_BASIC|2);
789 TESTSETRATE(IEEE80211_MODE_11B,
790 IEEE80211_RATE_BASIC|4);
791 TESTSETRATE(IEEE80211_MODE_11B,
792 IEEE80211_RATE_BASIC|11);
793 TESTSETRATE(IEEE80211_MODE_11B,
794 IEEE80211_RATE_BASIC|22);
796 if (ic->ic_modecaps & (1<<IEEE80211_MODE_11G)) {
797 TESTSETRATE(IEEE80211_MODE_11G, 47);
798 TESTSETRATE(IEEE80211_MODE_11G, 72);
799 TESTSETRATE(IEEE80211_MODE_11G, 96);
800 TESTSETRATE(IEEE80211_MODE_11G, 108);
802 if (ic->ic_modecaps & (1<<IEEE80211_MODE_11A)) {
803 TESTSETRATE(IEEE80211_MODE_11A, 47);
804 TESTSETRATE(IEEE80211_MODE_11A, 72);
805 TESTSETRATE(IEEE80211_MODE_11A, 96);
806 TESTSETRATE(IEEE80211_MODE_11A, 108);
808 #undef SETRATE
809 #undef INCRATE
811 * Taking yet more guesses here.
813 for (j = 1; j < IEEE80211_CHAN_MAX; j++) {
814 int chanflag = 0;
816 if (ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates)
817 chanflag |= IEEE80211_CHAN_G;
818 if (j <= 14)
819 chanflag |= IEEE80211_CHAN_B;
820 if (ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates &&
821 j > 14)
822 chanflag = IEEE80211_CHAN_A;
823 if (chanflag == 0)
824 break;
825 ic->ic_channels[j].ic_freq =
826 ieee80211_ieee2mhz(j, chanflag);
827 ic->ic_channels[j].ic_flags = chanflag;
830 j = sizeof(arg);
831 r = ndis_get_info(sc, OID_802_11_WEP_STATUS, &arg, &j);
832 if (arg != NDIS_80211_WEPSTAT_NOTSUPPORTED)
833 ic->ic_caps |= IEEE80211_C_WEP;
834 j = sizeof(arg);
835 r = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &j);
836 if (r == 0)
837 ic->ic_caps |= IEEE80211_C_PMGT;
838 memcpy(&ic->ic_myaddr, eaddr, sizeof(eaddr));
839 if_attach(ifp);
840 ieee80211_ifattach(&sc->ic);
841 ieee80211_media_init(&sc->ic, ieee80211_media_change,
842 ndis_media_status);
844 ic->ic_ibss_chan = IEEE80211_CHAN_ANYC;
845 ic->ic_bss->ni_chan = ic->ic_ibss_chan;
846 } else {
847 ifmedia_init(&sc->ifmedia, IFM_IMASK, ndis_ifmedia_upd,
848 ndis_ifmedia_sts);
849 ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
850 ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
851 ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL);
852 ifmedia_add(&sc->ifmedia,
853 IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
854 ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);
855 ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_AUTO);
856 if_attach(ifp);
857 ether_ifattach(ifp, eaddr);
860 /* Override the status handler so we can detect link changes. */
861 sc->ndis_block->nmb_status_func = ndis_linksts_wrap;
862 sc->ndis_block->nmb_statusdone_func = ndis_linksts_done_wrap;
863 fail:
864 if (error)
865 ndis_detach(dev, 0);
866 else
867 /* We're done talking to the NIC for now; halt it. */
868 ndis_halt_nic(sc);
870 return;
874 * Shutdown hardware and free up resources. This can be called any
875 * time after the mutex has been initialized. It is called in both
876 * the error case in attach and the normal detach case so it needs
877 * to be careful about only freeing resources that have actually been
878 * allocated.
881 ndis_detach (dev, flags)
882 device_t dev;
883 int flags;
885 struct ndis_softc *sc;
886 struct ifnet *ifp;
887 driver_object *drv;
888 int s;
890 #ifdef NDIS_DBG
891 printf("in ndis_detach\n");
892 #endif
894 sc = device_get_softc(dev);
896 NDIS_LOCK(sc);
898 ifp = &sc->arpcom.ac_if;
899 ifp->if_flags &= ~IFF_UP;
901 if (device_is_attached(dev)) {
902 NDIS_UNLOCK(sc);
903 ndis_stop(sc);
904 if (sc->ndis_80211)
905 ieee80211_ifdetach(&sc->ic);
906 else
907 ether_ifdetach(ifp);
908 } else {
909 NDIS_UNLOCK(sc);
914 * TODO: unmap interrupts when unloading in NetBSD
916 if (sc->ndis_res_io)
917 bus_release_resource(dev, SYS_RES_IOPORT,
918 sc->ndis_io_rid, sc->ndis_res_io);
919 if (sc->ndis_res_mem)
920 bus_release_resource(dev, SYS_RES_MEMORY,
921 sc->ndis_mem_rid, sc->ndis_res_mem);
922 if (sc->ndis_sc)
923 ndis_destroy_dma(sc);
925 if (sc->ndis_txarray)
926 free(sc->ndis_txarray, M_DEVBUF);
929 ndis_unload_driver((void *)ifp);
931 if (sc->ndis_txpool != NULL)
932 NdisFreePacketPool(sc->ndis_txpool);
934 /* Destroy the PDO for this device. */
936 if (sc->ndis_iftype == PCIBus)
937 drv = windrv_lookup(0, "PCI Bus");
938 else if (sc->ndis_iftype == PCMCIABus)
939 drv = windrv_lookup(0, "PCCARD Bus");
940 else
941 drv = windrv_lookup(0, "USB Bus");
942 if (drv == NULL)
943 panic("couldn't find driver object");
944 windrv_destroy_pdo(drv, dev);
946 * TODO: Unmap dma for NetBSD
949 return(0);
952 /* TODO: write a NetBSD version of ndis_suspend() */
954 /* TODO: write a NetBSD version of ndis_resume() */
957 * A frame has been uploaded: pass the resulting mbuf chain up to
958 * the higher level protocols.
960 * When handling received NDIS packets, the 'status' field in the
961 * out-of-band portion of the ndis_packet has special meaning. In the
962 * most common case, the underlying NDIS driver will set this field
963 * to NDIS_STATUS_SUCCESS, which indicates that it's ok for us to
964 * take posession of it. We then change the status field to
965 * NDIS_STATUS_PENDING to tell the driver that we now own the packet,
966 * and that we will return it at some point in the future via the
967 * return packet handler.
969 * If the driver hands us a packet with a status of NDIS_STATUS_RESOURCES,
970 * this means the driver is running out of packet/buffer resources and
971 * wants to maintain ownership of the packet. In this case, we have to
972 * copy the packet data into local storage and let the driver keep the
973 * packet.
975 __stdcall /*static*/ void
976 ndis_rxeof(ndis_handle adapter, ndis_packet **packets, uint32_t pktcnt)
978 struct ndis_softc *sc;
979 ndis_miniport_block *block;
980 ndis_packet *p;
981 uint32_t s;
982 ndis_tcpip_csum *csum;
983 struct ifnet *ifp;
984 struct mbuf *m0, *m;
985 int i;
987 block = (ndis_miniport_block *)adapter;
988 sc = (struct ndis_softc *)block->nmb_physdeviceobj->pdo_sc;
989 ifp = &sc->arpcom.ac_if;
991 for (i = 0; i < pktcnt; i++) {
992 p = packets[i];
993 /* Stash the softc here so ptom can use it. */
994 p->np_softc = sc;
995 if (ndis_ptom(&m0, p)) {
996 aprint_error_dev(sc->ndis_dev, "ptom failed\n");
997 if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS)
998 ndis_return_packet(NULL, (void *)sc, 0, p);
999 } else {
1000 if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) {
1001 m = m_dup(m0, 0, m0->m_pkthdr.len, FALSE);
1003 * NOTE: we want to destroy the mbuf here, but
1004 * we don't actually want to return it to the
1005 * driver via the return packet handler. By
1006 * bumping np_refcnt, we can prevent the
1007 * ndis_return_packet() routine from actually
1008 * doing anything.
1010 p->np_refcnt++;
1011 m_freem(m0);
1012 if (m == NULL)
1013 ifp->if_ierrors++;
1014 else
1015 m0 = m;
1016 } else
1017 p->np_oob.npo_status = NDIS_STATUS_PENDING;
1018 m0->m_pkthdr.rcvif = ifp;
1019 ifp->if_ipackets++;
1021 /* Deal with checksum offload. */
1023 * TODO: deal with checksum offload in NetBSD
1024 * (see IFCAP_XXX in sys/net/if.h, these differ from the FreeBSD ones)
1026 if (ifp->if_capenable & IFCAP_RXCSUM &&
1027 p->np_ext.npe_info[ndis_tcpipcsum_info] != NULL) {
1028 s = (uintptr_t)
1029 p->np_ext.npe_info[ndis_tcpipcsum_info];
1030 csum = (ndis_tcpip_csum *)&s;
1031 if (!(csum->u.ntc_rxflags &
1032 NDIS_RXCSUM_IP_PASSED))
1033 m0->m_pkthdr.csum_flags |=
1034 M_CSUM_IPv4_BAD;
1035 if (csum->u.ntc_rxflags &
1036 (NDIS_RXCSUM_TCP_PASSED |
1037 NDIS_RXCSUM_UDP_PASSED)) {
1038 //m0->m_pkthdr.csum_flags |=
1039 // CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
1040 m0->m_pkthdr.csum_data = 0xFFFF;
1044 if(ifp->if_bpf) {
1045 bpf_mtap(ifp->if_bpf, m0);
1048 (*ifp->if_input)(ifp, m0);
1052 return;
1056 * A frame was downloaded to the chip. It's safe for us to clean up
1057 * the list buffers.
1059 __stdcall /*static*/ void
1060 ndis_txeof(adapter, packet, status)
1061 ndis_handle adapter;
1062 ndis_packet *packet;
1063 ndis_status status;
1066 struct ndis_softc *sc;
1067 ndis_miniport_block *block;
1068 struct ifnet *ifp;
1069 int idx;
1070 struct mbuf *m;
1071 int s;
1073 block = (ndis_miniport_block *)adapter;
1074 sc = (struct ndis_softc *)block->nmb_physdeviceobj->pdo_sc;
1075 ifp = &sc->arpcom.ac_if;
1077 m = packet->np_m0;
1078 idx = packet->np_txidx;
1079 if (sc->ndis_sc)
1080 bus_dmamap_unload(sc->ndis_ttag, sc->ndis_tmaps[idx]);
1082 ndis_free_packet(packet);
1083 m_freem(m);
1085 NDIS_LOCK(sc);
1087 sc->ndis_txarray[idx] = NULL;
1088 sc->ndis_txpending++;
1090 if (status == NDIS_STATUS_SUCCESS)
1091 ifp->if_opackets++;
1092 else
1093 ifp->if_oerrors++;
1094 ifp->if_timer = 0;
1095 ifp->if_flags &= ~IFF_OACTIVE;
1096 NDIS_UNLOCK(sc);
1098 ndis_sched(ndis_starttask, ifp, NDIS_TASKQUEUE);
1100 return;
1103 __stdcall /*static*/ void
1104 ndis_linksts(ndis_handle adapter, ndis_status status, void *sbuf, uint32_t slen)
1106 ndis_miniport_block *block;
1107 struct ndis_softc *sc;
1109 block = adapter;
1110 sc = (struct ndis_softc *)block->nmb_physdeviceobj->pdo_sc;
1113 block->nmb_getstat = status;
1115 return;
1118 __stdcall /*static*/ void
1119 ndis_linksts_done(ndis_handle adapter)
1121 ndis_miniport_block *block;
1122 struct ndis_softc *sc;
1123 struct ifnet *ifp;
1125 block = adapter;
1126 sc = (struct ndis_softc *)block->nmb_physdeviceobj->pdo_sc;
1127 ifp = &sc->arpcom.ac_if;
1129 if (!NDIS_INITIALIZED(sc))
1130 return;
1132 switch (block->nmb_getstat) {
1133 case NDIS_STATUS_MEDIA_CONNECT:
1134 ndis_sched(ndis_ticktask, sc, NDIS_TASKQUEUE);
1135 ndis_sched(ndis_starttask, ifp, NDIS_TASKQUEUE);
1136 break;
1137 case NDIS_STATUS_MEDIA_DISCONNECT:
1138 if (sc->ndis_link)
1139 ndis_sched(ndis_ticktask, sc, NDIS_TASKQUEUE);
1140 break;
1141 default:
1142 break;
1145 return;
1149 ndis_intr(arg)
1150 void *arg;
1152 struct ndis_softc *sc;
1153 struct ifnet *ifp;
1154 int is_our_intr = 0;
1155 int call_isr = 0;
1156 uint8_t irql;
1157 ndis_miniport_interrupt *intr;
1159 sc = arg;
1160 ifp = &sc->arpcom.ac_if;
1163 * I was getting an interrupt before NdisAddDevice was called,
1164 * which sets up the ndis_block, so...
1166 if(sc->ndis_block == NULL) {
1167 return 0;
1170 intr = sc->ndis_block->nmb_interrupt;
1172 if (sc->ndis_block->nmb_miniportadapterctx == NULL) {
1173 return 0;
1176 KeAcquireSpinLock(&intr->ni_dpccountlock, &irql);
1177 if (sc->ndis_block->nmb_interrupt->ni_isrreq == TRUE)
1178 ndis_isr(sc, &is_our_intr, &call_isr);
1179 else {
1180 ndis_disable_intr(sc);
1181 call_isr = 1;
1183 KeReleaseSpinLock(&intr->ni_dpccountlock, irql);
1185 if ((is_our_intr || call_isr)) {
1186 ndis_in_isr = TRUE;
1187 IoRequestDpc(sc->ndis_block->nmb_deviceobj, NULL, sc);
1188 ndis_in_isr = FALSE;
1191 return 0;
1195 * just here so I can wake up the SWI thread
1196 * in ndis_ticktask
1198 struct ndisproc {
1199 struct ndisqhead *np_q;
1200 struct proc *np_p;
1201 int np_state;
1202 uint8_t np_stack[PAGE_SIZE*NDIS_KSTACK_PAGES];
1203 int np_needs_wakeup;
1205 extern struct ndisproc ndis_iproc;
1207 static void
1208 ndis_tick(void *xsc)
1210 struct ndis_softc *sc;
1212 /* TODO: do we need the lock for NetBSD? */
1214 sc = xsc;
1216 ndis_sched(ndis_ticktask, sc, NDIS_TASKQUEUE);
1218 callout_reset(&sc->ndis_stat_ch, hz *
1219 sc->ndis_block->nmb_checkforhangsecs, ndis_tick, sc);
1222 return;
1225 static void
1226 ndis_ticktask(void *xsc)
1228 struct ndis_softc *sc;
1229 __stdcall ndis_checkforhang_handler hangfunc;
1230 uint8_t rval;
1231 ndis_media_state linkstate;
1232 int error, len;
1233 int s;
1235 sc = xsc;
1237 hangfunc = sc->ndis_chars->nmc_checkhang_func;
1239 if (hangfunc != NULL) {
1240 rval = MSCALL1(hangfunc,
1241 sc->ndis_block->nmb_miniportadapterctx);
1242 if (rval == TRUE) {
1243 ndis_reset_nic(sc);
1244 return;
1248 len = sizeof(linkstate);
1249 error = ndis_get_info(sc, OID_GEN_MEDIA_CONNECT_STATUS,
1250 (void *)&linkstate, &len);
1252 NDIS_LOCK(sc);
1254 if (sc->ndis_link == 0 && linkstate == nmc_connected) {
1255 aprint_normal_dev(sc->ndis_dev, "link up\n");
1256 sc->ndis_link = 1;
1258 NDIS_UNLOCK(sc);
1259 if (sc->ndis_80211)
1260 ndis_getstate_80211(sc);
1261 NDIS_LOCK(sc);
1263 #ifdef LINK_STATE_UP
1264 sc->arpcom.ac_if.if_link_state = LINK_STATE_UP;
1265 rt_ifmsg(&(sc->arpcom.ac_if));
1266 #endif /* LINK_STATE_UP */
1269 if (sc->ndis_link == 1 && linkstate == nmc_disconnected) {
1270 aprint_normal_dev(sc->ndis_dev, "link down\n");
1271 sc->ndis_link = 0;
1272 #ifdef LINK_STATE_DOWN
1273 sc->arpcom.ac_if.if_link_state = LINK_STATE_DOWN;
1274 rt_ifmsg(&(sc->arpcom.ac_if));
1275 #endif /* LINK_STATE_DOWN */
1278 NDIS_UNLOCK(sc);
1280 return;
1283 static void
1284 ndis_map_sclist(arg, segs, nseg, mapsize, error)
1285 void *arg;
1286 bus_dma_segment_t *segs;
1287 int nseg;
1288 bus_size_t mapsize;
1289 int error;
1292 struct ndis_sc_list *sclist;
1293 int i;
1295 if (error || arg == NULL)
1296 return;
1298 sclist = arg;
1300 sclist->nsl_frags = nseg;
1302 for (i = 0; i < nseg; i++) {
1303 sclist->nsl_elements[i].nse_addr.np_quad = segs[i].ds_addr;
1304 sclist->nsl_elements[i].nse_len = segs[i].ds_len;
1307 return;
1310 static void
1311 ndis_starttask(void *arg)
1313 struct ifnet *ifp;
1315 ifp = arg;
1316 if (!IFQ_IS_EMPTY(&ifp->if_snd))
1317 ndis_start(ifp);
1318 return;
1322 * Main transmit routine. To make NDIS drivers happy, we need to
1323 * transform mbuf chains into NDIS packets and feed them to the
1324 * send packet routines. Most drivers allow you to send several
1325 * packets at once (up to the maxpkts limit). Unfortunately, rather
1326 * that accepting them in the form of a linked list, they expect
1327 * a contiguous array of pointers to packets.
1329 * For those drivers which use the NDIS scatter/gather DMA mechanism,
1330 * we need to perform busdma work here. Those that use map registers
1331 * will do the mapping themselves on a buffer by buffer basis.
1333 static void
1334 ndis_start(struct ifnet *ifp)
1336 struct ndis_softc *sc;
1337 struct mbuf *m = NULL;
1338 ndis_packet **p0 = NULL, *p = NULL;
1339 ndis_tcpip_csum *csum;
1340 int pcnt = 0, status;
1341 int s;
1343 sc = ifp->if_softc;
1345 NDIS_LOCK(sc);
1347 if (!sc->ndis_link || ifp->if_flags & IFF_OACTIVE) {
1348 NDIS_UNLOCK(sc);
1349 return;
1352 p0 = &sc->ndis_txarray[sc->ndis_txidx];
1354 while(sc->ndis_txpending) {
1355 IFQ_DEQUEUE(&ifp->if_snd, m);
1356 if (m == NULL)
1357 break;
1359 NdisAllocatePacket(&status,
1360 &sc->ndis_txarray[sc->ndis_txidx], sc->ndis_txpool);
1362 if (status != NDIS_STATUS_SUCCESS)
1363 break;
1365 if (ndis_mtop(m, &sc->ndis_txarray[sc->ndis_txidx])) {
1366 #if __FreeBSD_version >= 502114
1367 IFQ_DRV_PREPEND(&ifp->if_snd, m);
1368 #endif
1369 NDIS_UNLOCK(sc);
1370 #if __FreeBSD_version < 502114
1371 IF_PREPEND(&ifp->if_snd, m);
1372 #endif
1373 return;
1377 * Save pointer to original mbuf
1378 * so we can free it later.
1381 p = sc->ndis_txarray[sc->ndis_txidx];
1382 p->np_txidx = sc->ndis_txidx;
1383 p->np_m0 = m;
1384 p->np_oob.npo_status = NDIS_STATUS_PENDING;
1387 * Do scatter/gather processing, if driver requested it.
1389 if (sc->ndis_sc) {
1391 * TODO: NetBSD's bus_dmamap_load_mbuf dosen't provide a callback function
1392 * argumet as FreeBSD's does figure out what to do about this.
1394 bus_dmamap_load_mbuf(sc->ndis_ttag,
1395 sc->ndis_tmaps[sc->ndis_txidx], m,
1396 BUS_DMA_WRITE|BUS_DMA_NOWAIT);
1397 /* Just call the callback function ? */
1398 ndis_map_sclist(&p->np_sclist,
1399 sc->ndis_tmaps[sc->ndis_txidx]->dm_segs,
1400 sc->ndis_tmaps[sc->ndis_txidx]->dm_nsegs,
1401 sc->ndis_tmaps[sc->ndis_txidx]->dm_mapsize, 0);
1403 * TODO: Need an offset and length to pass to bus_dmamap_sync() (not needed in
1404 * FreeBSD), I'm not sure I did this correctly, as man 9 bus_dma says that
1405 * dm_segs is "an array of segments or a pointer to an array of segments".
1407 bus_dmamap_sync(sc->ndis_ttag,
1408 sc->ndis_tmaps[sc->ndis_txidx],
1409 sc->ndis_tmaps[sc->ndis_txidx]->dm_segs->ds_addr,
1410 sc->ndis_tmaps[sc->ndis_txidx]->dm_segs->ds_len,
1411 BUS_DMASYNC_PREREAD);
1412 p->np_ext.npe_info[ndis_sclist_info] = &p->np_sclist;
1415 /* Handle checksum offload. */
1416 if (ifp->if_capenable & IFCAP_TXCSUM &&
1417 m->m_pkthdr.csum_flags) {
1418 csum = (ndis_tcpip_csum *)
1419 &p->np_ext.npe_info[ndis_tcpipcsum_info];
1420 csum->u.ntc_txflags = NDIS_TXCSUM_DO_IPV4;
1421 if (m->m_pkthdr.csum_flags & CSUM_IP)
1422 csum->u.ntc_txflags |= NDIS_TXCSUM_DO_IP;
1423 if (m->m_pkthdr.csum_flags & CSUM_TCP)
1424 csum->u.ntc_txflags |= NDIS_TXCSUM_DO_TCP;
1425 if (m->m_pkthdr.csum_flags & CSUM_UDP)
1426 csum->u.ntc_txflags |= NDIS_TXCSUM_DO_UDP;
1427 p->np_private.npp_flags = NDIS_PROTOCOL_ID_TCP_IP;
1430 NDIS_INC(sc);
1431 sc->ndis_txpending--;
1433 pcnt++;
1436 * If there's a BPF listener, bounce a copy of this frame
1437 * to him.
1439 bpf_mtap(ifp, m);
1441 * The array that p0 points to must appear contiguous,
1442 * so we must not wrap past the end of sc->ndis_txarray[].
1443 * If it looks like we're about to wrap, break out here
1444 * so the this batch of packets can be transmitted, then
1445 * wait for txeof to ask us to send the rest.
1448 if (sc->ndis_txidx == 0)
1449 break;
1452 if (pcnt == 0) {
1453 NDIS_UNLOCK(sc);
1454 return;
1457 if (sc->ndis_txpending == 0)
1458 ifp->if_flags |= IFF_OACTIVE;
1461 * Set a timeout in case the chip goes out to lunch.
1463 ifp->if_timer = 5;
1465 NDIS_UNLOCK(sc);
1467 if (sc->ndis_maxpkts == 1)
1468 ndis_send_packet(sc, p);
1469 else
1470 ndis_send_packets(sc, p0, pcnt);
1472 return;
1475 static int
1476 ndis_init(xsc)
1477 struct ifnet *xsc;
1479 struct ndis_softc *sc = xsc->if_softc;
1480 struct ifnet *ifp = xsc;
1481 int s;
1482 int i, error;
1485 * Avoid reintializing the link unnecessarily.
1486 * This should be dealt with in a better way by
1487 * fixing the upper layer modules so they don't
1488 * call ifp->if_init() quite as often.
1490 if (sc->ndis_link && sc->ndis_skip)
1491 return 0;
1494 * Cancel pending I/O and free all RX/TX buffers.
1496 ndis_stop(sc);
1497 if (ndis_init_nic(sc)) {
1498 return 0;
1501 /* Init our MAC address */
1503 /* Program the packet filter */
1505 sc->ndis_filter = NDIS_PACKET_TYPE_DIRECTED;
1507 if (ifp->if_flags & IFF_BROADCAST)
1508 sc->ndis_filter |= NDIS_PACKET_TYPE_BROADCAST;
1510 if (ifp->if_flags & IFF_PROMISC)
1511 sc->ndis_filter |= NDIS_PACKET_TYPE_PROMISCUOUS;
1513 i = sizeof(sc->ndis_filter);
1515 error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER,
1516 &sc->ndis_filter, &i);
1518 if (error)
1519 aprint_error_dev(sc->ndis_dev, "set filter failed: %d\n",
1520 error);
1523 * Program the multicast filter, if necessary.
1525 ndis_setmulti(sc);
1527 /* Setup task offload. */
1528 ndis_set_offload(sc);
1530 /* Enable interrupts. */
1531 ndis_enable_intr(sc);
1533 if (sc->ndis_80211)
1534 ndis_setstate_80211(sc);
1536 NDIS_LOCK(sc);
1538 sc->ndis_txidx = 0;
1539 sc->ndis_txpending = sc->ndis_maxpkts;
1540 sc->ndis_link = 0;
1542 ifp->if_flags |= IFF_RUNNING;
1543 ifp->if_flags &= ~IFF_OACTIVE;
1545 NDIS_UNLOCK(sc);
1548 * Some drivers don't set this value. The NDIS spec says
1549 * the default checkforhang timeout is "approximately 2
1550 * seconds." We use 3 seconds, because it seems for some
1551 * drivers, exactly 2 seconds is too fast.
1554 if (sc->ndis_block->nmb_checkforhangsecs == 0)
1555 sc->ndis_block->nmb_checkforhangsecs = 3;
1557 callout_reset(&sc->ndis_stat_ch,
1558 hz * sc->ndis_block->nmb_checkforhangsecs,
1559 ndis_tick, sc);
1561 return 0;
1565 * Set media options.
1567 static int
1568 ndis_ifmedia_upd(struct ifnet *ifp)
1570 struct ndis_softc *sc;
1572 sc = ifp->if_softc;
1574 if (NDIS_INITIALIZED(sc))
1575 ndis_init(&sc->arpcom.ac_if);
1577 return(0);
1581 * Report current media status.
1583 static void
1584 ndis_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
1586 struct ndis_softc *sc;
1587 uint32_t media_info;
1588 ndis_media_state linkstate;
1589 int error, len;
1591 ifmr->ifm_status = IFM_AVALID;
1592 ifmr->ifm_active = IFM_ETHER;
1593 sc = ifp->if_softc;
1595 if (!NDIS_INITIALIZED(sc))
1596 return;
1598 len = sizeof(linkstate);
1599 error = ndis_get_info(sc, OID_GEN_MEDIA_CONNECT_STATUS,
1600 (void *)&linkstate, &len);
1602 len = sizeof(media_info);
1603 error = ndis_get_info(sc, OID_GEN_LINK_SPEED,
1604 (void *)&media_info, &len);
1606 if (linkstate == nmc_connected)
1607 ifmr->ifm_status |= IFM_ACTIVE;
1609 switch(media_info) {
1610 case 100000:
1611 ifmr->ifm_active |= IFM_10_T;
1612 break;
1613 case 1000000:
1614 ifmr->ifm_active |= IFM_100_TX;
1615 break;
1616 case 10000000:
1617 ifmr->ifm_active |= IFM_1000_T;
1618 break;
1619 default:
1620 aprint_error_dev(sc->ndis_dev, "unknown speed: %d\n",
1621 media_info);
1622 break;
1625 return;
1628 /* TODO: Perhaps raise the IPL while in these wireless functions ? */
1630 static void
1631 ndis_setstate_80211(struct ndis_softc *sc)
1633 struct ieee80211com *ic;
1634 ndis_80211_ssid ssid;
1635 ndis_80211_config config;
1636 ndis_80211_wep wep;
1637 int i, rval = 0, len;
1638 uint32_t arg;
1639 struct ifnet *ifp;
1641 #define wk_len wk_keylen
1642 #define ic_wep_txkey ic_def_txkey
1644 ic = &sc->ic;
1646 /* TODO: are these equivelant? */
1647 ifp = sc->ic.ic_ifp;
1649 if (!NDIS_INITIALIZED(sc))
1650 return;
1652 /* Set network infrastructure mode. */
1654 len = sizeof(arg);
1655 if (ic->ic_opmode == IEEE80211_M_IBSS)
1656 arg = NDIS_80211_NET_INFRA_IBSS;
1657 else
1658 arg = NDIS_80211_NET_INFRA_BSS;
1660 rval = ndis_set_info(sc, OID_802_11_INFRASTRUCTURE_MODE, &arg, &len);
1662 if (rval)
1663 aprint_error_dev(sc->ndis_dev, "set infra failed: %d\n", rval);
1665 /* Set WEP */
1666 /* TODO: Clean up these #ifdef's */
1667 if (ic->ic_flags & IEEE80211_F_PRIVACY) {
1668 for (i = 0; i < IEEE80211_WEP_NKID; i++) {
1669 if (ic->ic_nw_keys[i].wk_len) {
1670 memset((char *)&wep, 0, sizeof(wep));
1671 wep.nw_keylen = ic->ic_nw_keys[i].wk_len;
1672 #ifdef notdef
1673 /* 5 and 13 are the only valid key lengths */
1674 if (ic->ic_nw_keys[i].wk_len < 5)
1675 wep.nw_keylen = 5;
1676 else if (ic->ic_nw_keys[i].wk_len > 5 &&
1677 ic->ic_nw_keys[i].wk_len < 13)
1678 wep.nw_keylen = 13;
1679 #endif
1680 wep.nw_keyidx = i;
1681 wep.nw_length = (sizeof(uint32_t) * 3)
1682 + wep.nw_keylen;
1683 if (i == ic->ic_wep_txkey)
1684 wep.nw_keyidx |= NDIS_80211_WEPKEY_TX;
1685 bcopy(ic->ic_nw_keys[i].wk_key,
1686 wep.nw_keydata, wep.nw_length);
1687 len = sizeof(wep);
1688 rval = ndis_set_info(sc,
1689 OID_802_11_ADD_WEP, &wep, &len);
1690 if (rval)
1691 aprint_error_dev(sc->ndis_dev, "set wepkey failed: %d\n", rval);
1694 arg = NDIS_80211_WEPSTAT_ENABLED;
1695 len = sizeof(arg);
1696 rval = ndis_set_info(sc, OID_802_11_WEP_STATUS, &arg, &len);
1697 if (rval)
1698 aprint_error_dev(sc->ndis_dev, "enable WEP failed: %d\n",
1699 rval);
1700 arg = NDIS_80211_PRIVFILT_8021XWEP;
1701 len = sizeof(arg);
1702 rval = ndis_set_info(sc, OID_802_11_PRIVACY_FILTER, &arg, &len);
1703 #ifdef IEEE80211_WEP_8021X /*IEEE80211_F_WEPON*/
1704 /* Accept that we only have "shared" and 802.1x modes. */
1705 if (rval == 0) {
1706 if (arg == NDIS_80211_PRIVFILT_ACCEPTALL)
1707 ic->ic_wep_mode = IEEE80211_WEP_MIXED;
1708 else
1709 ic->ic_wep_mode = IEEE80211_WEP_8021X;
1711 #endif
1712 arg = NDIS_80211_AUTHMODE_OPEN;
1713 } else {
1714 arg = NDIS_80211_WEPSTAT_DISABLED;
1715 len = sizeof(arg);
1716 ndis_set_info(sc, OID_802_11_WEP_STATUS, &arg, &len);
1717 arg = NDIS_80211_AUTHMODE_OPEN;
1720 len = sizeof(arg);
1721 rval = ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len);
1723 #ifdef notyet
1724 if (rval)
1725 aprint_error_dev(sc->ndis_dev, "set auth failed: %d\n", rval);
1726 #endif
1728 #ifdef notyet
1729 /* Set network type. */
1731 arg = 0;
1733 switch (ic->ic_curmode) {
1734 case IEEE80211_MODE_11A:
1735 arg = NDIS_80211_NETTYPE_11OFDM5;
1736 break;
1737 case IEEE80211_MODE_11B:
1738 arg = NDIS_80211_NETTYPE_11DS;
1739 break;
1740 case IEEE80211_MODE_11G:
1741 arg = NDIS_80211_NETTYPE_11OFDM24;
1742 break;
1743 default:
1744 aprint_error_dev(sc->ndis_dev, "unknown mode: %d\n",
1745 ic->ic_curmode);
1748 if (arg) {
1749 len = sizeof(arg);
1750 rval = ndis_set_info(sc, OID_802_11_NETWORK_TYPE_IN_USE,
1751 &arg, &len);
1752 if (rval)
1753 aprint_error_dev(sc->ndis_dev, "set nettype failed: %d\n",
1754 rval);
1756 #endif
1758 len = sizeof(config);
1759 memset((char *)&config, 0, len);
1760 config.nc_length = len;
1761 config.nc_fhconfig.ncf_length = sizeof(ndis_80211_config_fh);
1762 rval = ndis_get_info(sc, OID_802_11_CONFIGURATION, &config, &len);
1765 * Some drivers expect us to initialize these values, so
1766 * provide some defaults.
1768 if (config.nc_beaconperiod == 0)
1769 config.nc_beaconperiod = 100;
1770 if (config.nc_atimwin == 0)
1771 config.nc_atimwin = 100;
1772 if (config.nc_fhconfig.ncf_dwelltime == 0)
1773 config.nc_fhconfig.ncf_dwelltime = 200;
1775 if (rval == 0 && ic->ic_ibss_chan != IEEE80211_CHAN_ANYC) {
1776 int chan, chanflag;
1778 chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
1779 chanflag = config.nc_dsconfig > 2500000 ? IEEE80211_CHAN_2GHZ :
1780 IEEE80211_CHAN_5GHZ;
1781 if (chan != ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0)) {
1782 config.nc_dsconfig =
1783 ic->ic_ibss_chan->ic_freq * 1000;
1784 ic->ic_bss->ni_chan = ic->ic_ibss_chan;
1785 len = sizeof(config);
1786 config.nc_length = len;
1787 config.nc_fhconfig.ncf_length =
1788 sizeof(ndis_80211_config_fh);
1789 rval = ndis_set_info(sc, OID_802_11_CONFIGURATION,
1790 &config, &len);
1791 if (rval)
1792 aprint_error_dev(sc->ndis_dev, "couldn't change "
1793 "DS config to %ukHz: %d\n",
1794 config.nc_dsconfig,
1795 rval);
1797 } else if (rval)
1798 aprint_error_dev(sc->ndis_dev, "couldn't retrieve "
1799 "channel info: %d\n", rval);
1801 /* Set SSID -- always do this last. */
1803 len = sizeof(ssid);
1804 memset((char *)&ssid, 0, len);
1805 ssid.ns_ssidlen = ic->ic_des_esslen;
1806 if (ssid.ns_ssidlen == 0) {
1807 ssid.ns_ssidlen = 1;
1808 } else
1809 memcpy(ssid.ns_ssid, ic->ic_des_essid, ssid.ns_ssidlen);
1810 rval = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len);
1812 if (rval)
1813 aprint_error_dev(sc->ndis_dev, "set ssid failed: %d\n", rval);
1815 return;
1818 static void
1819 ndis_media_status(struct ifnet *ifp, struct ifmediareq *imr)
1821 struct ieee80211com *ic = (void *)ifp;
1822 struct ieee80211_node *ni = NULL;
1824 imr->ifm_status = IFM_AVALID;
1825 imr->ifm_active = IFM_IEEE80211;
1826 if (ic->ic_state == IEEE80211_S_RUN)
1827 imr->ifm_status |= IFM_ACTIVE;
1828 imr->ifm_active |= IFM_AUTO;
1829 switch (ic->ic_opmode) {
1830 case IEEE80211_M_STA:
1831 ni = ic->ic_bss;
1832 /* calculate rate subtype */
1833 imr->ifm_active |= ieee80211_rate2media(ic,
1834 ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
1835 break;
1836 case IEEE80211_M_IBSS:
1837 ni = ic->ic_bss;
1838 /* calculate rate subtype */
1839 imr->ifm_active |= ieee80211_rate2media(ic,
1840 ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
1841 imr->ifm_active |= IFM_IEEE80211_ADHOC;
1842 break;
1843 case IEEE80211_M_AHDEMO:
1844 /* should not come here */
1845 break;
1846 case IEEE80211_M_HOSTAP:
1847 imr->ifm_active |= IFM_IEEE80211_HOSTAP;
1848 break;
1849 case IEEE80211_M_MONITOR:
1850 imr->ifm_active |= IFM_IEEE80211_MONITOR;
1851 break;
1853 switch (ic->ic_curmode) {
1854 case IEEE80211_MODE_11A:
1855 imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A);
1856 break;
1857 case IEEE80211_MODE_11B:
1858 imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11B);
1859 break;
1860 case IEEE80211_MODE_11G:
1861 imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11G);
1862 break;
1864 * TODO: is this correct? (IEEE80211_MODE_TURBO_A and IEEE80211_MODE_TURBO_G
1865 * are defined in _ieee80211.h)
1867 case IEEE80211_MODE_TURBO_A:
1868 case IEEE80211_MODE_TURBO_G:
1869 imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A)
1870 | IFM_IEEE80211_TURBO;
1871 break;
1875 static int
1876 ndis_get_assoc(struct ndis_softc *sc, ndis_wlan_bssid_ex **assoc)
1878 ndis_80211_bssid_list_ex *bl;
1879 ndis_wlan_bssid_ex *bs;
1880 ndis_80211_macaddr bssid;
1881 int i, len, error;
1883 if (!sc->ndis_link)
1884 return(ENOENT);
1886 len = sizeof(bssid);
1887 error = ndis_get_info(sc, OID_802_11_BSSID, &bssid, &len);
1888 if (error) {
1889 aprint_error_dev(sc->ndis_dev, "failed to get bssid\n");
1890 return(ENOENT);
1892 len = 0;
1893 error = ndis_get_info(sc, OID_802_11_BSSID_LIST, NULL, &len);
1894 if (error != ENOSPC) {
1895 aprint_error_dev(sc->ndis_dev, "bssid_list failed\n");
1896 return (error);
1899 bl = malloc(len, M_TEMP, M_NOWAIT|M_ZERO);
1900 error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len);
1901 if (error) {
1902 free(bl, M_TEMP);
1903 aprint_error_dev(sc->ndis_dev, "bssid_list failed\n");
1904 return (error);
1907 bs = (ndis_wlan_bssid_ex *)&bl->nblx_bssid[0];
1908 for (i = 0; i < bl->nblx_items; i++) {
1909 if (memcmp(bs->nwbx_macaddr, bssid, sizeof(bssid)) == 0) {
1910 *assoc = malloc(bs->nwbx_len, M_TEMP, M_NOWAIT);
1911 if (*assoc == NULL) {
1912 free(bl, M_TEMP);
1913 return(ENOMEM);
1915 memcpy((char *)*assoc, (char *)bs, bs->nwbx_len);
1916 free(bl, M_TEMP);
1917 return(0);
1919 bs = (ndis_wlan_bssid_ex *)((char *)bs + bs->nwbx_len);
1922 free(bl, M_TEMP);
1923 return(ENOENT);
1926 static void
1927 ndis_getstate_80211(struct ndis_softc *sc)
1929 struct ieee80211com *ic;
1930 ndis_80211_ssid ssid;
1931 ndis_80211_config config;
1932 ndis_wlan_bssid_ex *bs = 0;
1933 int rval, len, i = 0;
1934 uint32_t arg;
1935 struct ifnet *ifp;
1937 ic = &sc->ic;
1938 /* TODO: are these equivelant? */
1939 ifp = sc->ic.ic_ifp;
1941 if (!NDIS_INITIALIZED(sc))
1942 return;
1944 if (sc->ndis_link)
1945 ic->ic_state = IEEE80211_S_RUN;
1946 else
1947 ic->ic_state = IEEE80211_S_ASSOC;
1951 * If we're associated, retrieve info on the current bssid.
1953 if ((rval = ndis_get_assoc(sc, &bs)) == 0) {
1954 switch(bs->nwbx_nettype) {
1955 case NDIS_80211_NETTYPE_11FH:
1956 case NDIS_80211_NETTYPE_11DS:
1957 ic->ic_curmode = IEEE80211_MODE_11B;
1958 break;
1959 case NDIS_80211_NETTYPE_11OFDM5:
1960 ic->ic_curmode = IEEE80211_MODE_11A;
1961 break;
1962 case NDIS_80211_NETTYPE_11OFDM24:
1963 ic->ic_curmode = IEEE80211_MODE_11G;
1964 break;
1965 default:
1966 aprint_error_dev(sc->ndis_dev, "unknown nettype %d\n",
1967 arg);
1968 break;
1970 free(bs, M_TEMP);
1971 } else
1972 return;
1974 len = sizeof(ssid);
1975 memset((char *)&ssid, 0, len);
1976 rval = ndis_get_info(sc, OID_802_11_SSID, &ssid, &len);
1978 if (rval)
1979 aprint_error_dev(sc->ndis_dev, "get ssid failed: %d\n", rval);
1980 memcpy(ic->ic_bss->ni_essid, ssid.ns_ssid, ssid.ns_ssidlen);
1981 ic->ic_bss->ni_esslen = ssid.ns_ssidlen;
1983 len = sizeof(arg);
1984 rval = ndis_get_info(sc, OID_GEN_LINK_SPEED, &arg, &len);
1985 if (rval)
1986 aprint_error_dev(sc->ndis_dev, "get link speed failed: %d\n",
1987 rval);
1989 if (ic->ic_modecaps & (1<<IEEE80211_MODE_11B)) {
1990 ic->ic_bss->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B];
1991 for (i = 0; i < ic->ic_bss->ni_rates.rs_nrates; i++) {
1992 if ((ic->ic_bss->ni_rates.rs_rates[i] &
1993 IEEE80211_RATE_VAL) == arg / 5000)
1994 break;
1998 if (i == ic->ic_bss->ni_rates.rs_nrates &&
1999 ic->ic_modecaps & (1<<IEEE80211_MODE_11G)) {
2000 ic->ic_bss->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11G];
2001 for (i = 0; i < ic->ic_bss->ni_rates.rs_nrates; i++) {
2002 if ((ic->ic_bss->ni_rates.rs_rates[i] &
2003 IEEE80211_RATE_VAL) == arg / 5000)
2004 break;
2008 if (i == ic->ic_bss->ni_rates.rs_nrates)
2009 aprint_error_dev(sc->ndis_dev, "no matching rate for: %d\n",
2010 arg / 5000);
2011 else
2012 ic->ic_bss->ni_txrate = i;
2014 if (ic->ic_caps & IEEE80211_C_PMGT) {
2015 len = sizeof(arg);
2016 rval = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &len);
2018 if (rval)
2019 aprint_error_dev(sc->ndis_dev, "get power mode failed: %d\n",
2020 rval);
2021 if (arg == NDIS_80211_POWERMODE_CAM)
2022 ic->ic_flags &= ~IEEE80211_F_PMGTON;
2023 else
2024 ic->ic_flags |= IEEE80211_F_PMGTON;
2027 len = sizeof(config);
2028 memset((char *)&config, 0, len);
2029 config.nc_length = len;
2030 config.nc_fhconfig.ncf_length = sizeof(ndis_80211_config_fh);
2031 rval = ndis_get_info(sc, OID_802_11_CONFIGURATION, &config, &len);
2032 if (rval == 0) {
2033 int chan;
2035 chan = ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0);
2036 if (chan < 0 || chan >= IEEE80211_CHAN_MAX) {
2037 if (ifp->if_flags & IFF_DEBUG)
2038 aprint_error_dev(sc->ndis_dev, "current channel "
2039 "(%uMHz) out of bounds\n",
2040 config.nc_dsconfig / 1000);
2041 ic->ic_bss->ni_chan = &ic->ic_channels[1];
2042 } else
2043 ic->ic_bss->ni_chan = &ic->ic_channels[chan];
2044 } else
2045 aprint_error_dev(sc->ndis_dev, "couldn't retrieve "
2046 "channel info: %d\n", rval);
2049 len = sizeof(arg);
2050 rval = ndis_get_info(sc, OID_802_11_WEP_STATUS, &arg, &len);
2052 if (rval)
2053 device_printf (sc->ndis_dev,
2054 "get wep status failed: %d\n", rval);
2056 if (arg == NDIS_80211_WEPSTAT_ENABLED)
2057 ic->ic_flags |= IEEE80211_F_WEPON;
2058 else
2059 ic->ic_flags &= ~IEEE80211_F_WEPON;
2061 return;
2064 static int
2065 ndis_ioctl(struct ifnet *ifp, u_long command, void *data)
2067 struct ndis_softc *sc = ifp->if_softc;
2068 struct ifreq *ifr = (struct ifreq *) data;
2069 int i, error = 0;
2070 int s;
2072 /*NDIS_LOCK(sc);*/
2073 s = splnet();
2075 switch(command) {
2076 case SIOCSIFFLAGS:
2077 if ((error = ifioctl_common(ifp, command, data)) != 0)
2078 break;
2079 if (ifp->if_flags & IFF_UP) {
2080 if (ifp->if_flags & IFF_RUNNING &&
2081 ifp->if_flags & IFF_PROMISC &&
2082 !(sc->ndis_if_flags & IFF_PROMISC)) {
2083 sc->ndis_filter |=
2084 NDIS_PACKET_TYPE_PROMISCUOUS;
2085 i = sizeof(sc->ndis_filter);
2086 error = ndis_set_info(sc,
2087 OID_GEN_CURRENT_PACKET_FILTER,
2088 &sc->ndis_filter, &i);
2089 } else if (ifp->if_flags & IFF_RUNNING &&
2090 !(ifp->if_flags & IFF_PROMISC) &&
2091 sc->ndis_if_flags & IFF_PROMISC) {
2092 sc->ndis_filter &=
2093 ~NDIS_PACKET_TYPE_PROMISCUOUS;
2094 i = sizeof(sc->ndis_filter);
2095 error = ndis_set_info(sc,
2096 OID_GEN_CURRENT_PACKET_FILTER,
2097 &sc->ndis_filter, &i);
2098 } else
2099 ndis_init(ifp);
2100 } else {
2101 if (ifp->if_flags & IFF_RUNNING)
2102 ndis_stop(sc);
2104 sc->ndis_if_flags = ifp->if_flags;
2105 error = 0;
2106 break;
2107 case SIOCADDMULTI:
2108 case SIOCDELMULTI:
2110 * TODO: I'm really not sure this is the correct thing to do here, but multicast
2111 * address lists weren't getting set in ether_ioctl because they SIOCADDMULTI
2112 * is routed to ndis_setmulti here.
2114 error = ether_ioctl(ifp, command, data);
2115 ndis_setmulti(sc);
2116 error = 0;
2117 break;
2118 case SIOCGIFMEDIA:
2119 case SIOCSIFMEDIA:
2120 if (sc->ndis_80211) {
2121 error = ieee80211_ioctl(&sc->ic, command, data);
2122 if (error == ENETRESET) {
2123 ndis_setstate_80211(sc);
2124 /*ndis_init(sc);*/
2125 error = 0;
2127 } else
2128 error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command);
2129 break;
2130 case SIOCSIFCAP:
2131 if ((error = ether_ioctl(ifp, command, data)) == ENETRESET) {
2132 ndis_set_offload(sc);
2133 error = 0;
2135 break;
2136 case SIOCGIFGENERIC:
2137 case SIOCSIFGENERIC:
2138 if (sc->ndis_80211 && NDIS_INITIALIZED(sc)) {
2139 if (command == SIOCGIFGENERIC)
2140 error = ndis_wi_ioctl_get(ifp, command, data);
2141 else
2142 error = ndis_wi_ioctl_set(ifp, command, data);
2143 } else
2144 error = ENOTTY;
2145 if (error != ENOTTY)
2146 break;
2147 default:
2148 sc->ndis_skip = 1;
2149 if (sc->ndis_80211) {
2150 error = ieee80211_ioctl(&sc->ic, command, data);
2152 if (error == ENETRESET) {
2153 ndis_setstate_80211(sc);
2154 error = 0;
2156 } else {
2157 error = ether_ioctl(ifp, command, data);
2159 sc->ndis_skip = 0;
2160 break;
2163 /*NDIS_UNLOCK(sc);*/
2164 splx(s);
2166 return(error);
2169 static int
2170 ndis_wi_ioctl_get(struct ifnet *ifp, u_long command, void * data)
2172 struct wi_req wreq;
2173 struct ifreq *ifr;
2174 struct ndis_softc *sc;
2175 ndis_80211_bssid_list_ex *bl;
2176 ndis_wlan_bssid_ex *wb;
2177 struct wi_apinfo *api;
2178 int error, i, j, len, maxaps;
2180 sc = ifp->if_softc;
2181 ifr = (struct ifreq *)data;
2182 error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
2183 if (error)
2184 return (error);
2186 switch (wreq.wi_type) {
2187 case WI_RID_READ_APS:
2188 len = 0;
2189 error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN,
2190 NULL, &len);
2191 if (error == 0)
2192 tsleep(&error, PPAUSE|PCATCH, "ssidscan", hz * 2);
2193 len = 0;
2194 error = ndis_get_info(sc, OID_802_11_BSSID_LIST, NULL, &len);
2195 if (error != ENOSPC)
2196 break;
2197 bl = malloc(len, M_DEVBUF, M_WAITOK|M_ZERO);
2198 error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len);
2199 if (error) {
2200 free(bl, M_DEVBUF);
2201 break;
2203 maxaps = (2 * wreq.wi_len - sizeof(int)) / sizeof(*api);
2204 maxaps = MIN(maxaps, bl->nblx_items);
2205 wreq.wi_len = (maxaps * sizeof(*api) + sizeof(int)) / 2;
2206 *(int *)&wreq.wi_val = maxaps;
2207 api = (struct wi_apinfo *)&((int *)&wreq.wi_val)[1];
2208 wb = bl->nblx_bssid;
2209 while (maxaps--) {
2210 memset(api, 0, sizeof(*api));
2211 memcpy(&api->bssid, &wb->nwbx_macaddr,
2212 sizeof(api->bssid));
2213 api->namelen = wb->nwbx_ssid.ns_ssidlen;
2214 memcpy(&api->name, &wb->nwbx_ssid.ns_ssid, api->namelen);
2215 if (wb->nwbx_privacy)
2216 api->capinfo |= IEEE80211_CAPINFO_PRIVACY;
2217 /* XXX Where can we get noise information? */
2218 api->signal = wb->nwbx_rssi + 149; /* XXX */
2219 api->quality = api->signal;
2220 api->channel =
2221 ieee80211_mhz2ieee(wb->nwbx_config.nc_dsconfig /
2222 1000, 0);
2223 /* In "auto" infrastructure mode, this is useless. */
2224 if (wb->nwbx_netinfra == NDIS_80211_NET_INFRA_IBSS)
2225 api->capinfo |= IEEE80211_CAPINFO_IBSS;
2226 if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) {
2227 j = sizeof(ndis_80211_rates_ex);
2228 /* handle other extended things */
2229 } else
2230 j = sizeof(ndis_80211_rates);
2231 for (i = api->rate = 0; i < j; i++)
2232 api->rate = MAX(api->rate, 5 *
2233 (wb->nwbx_supportedrates[i] & 0x7f));
2234 api++;
2235 wb = (ndis_wlan_bssid_ex *)((char *)wb + wb->nwbx_len);
2237 free(bl, M_DEVBUF);
2238 error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
2239 break;
2240 default:
2241 error = ENOTTY;
2242 break;
2244 return (error);
2247 static int
2248 ndis_wi_ioctl_set(struct ifnet *ifp, u_long command, void * data)
2250 struct wi_req wreq;
2251 struct ifreq *ifr;
2252 struct ndis_softc *sc;
2253 uint32_t foo;
2254 int error, len;
2256 error = kauth_authorize_network(kauth_cred_get(),
2257 KAUTH_NETWORK_INTERFACE, KAUTH_REQ_NETWORK_INTERFACE_SETPRIV,
2258 ifp, KAUTH_ARG(command), NULL);
2259 if (error)
2260 return (error);
2262 sc = ifp->if_softc;
2263 ifr = (struct ifreq *)data;
2264 error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
2265 if (error)
2266 return (error);
2268 switch (wreq.wi_type) {
2269 case WI_RID_SCAN_APS:
2270 case WI_RID_SCAN_REQ: /* arguments ignored */
2271 len = sizeof(foo);
2272 foo = 0;
2273 error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN, &foo,
2274 &len);
2275 break;
2276 default:
2277 error = ENOTTY;
2278 break;
2280 return (error);
2283 static void
2284 ndis_watchdog(struct ifnet *ifp)
2286 struct ndis_softc *sc;
2287 int s;
2289 sc = ifp->if_softc;
2291 NDIS_LOCK(sc);
2293 ifp->if_oerrors++;
2294 aprint_error_dev(sc->ndis_dev, "watchdog timeout\n");
2296 NDIS_UNLOCK(sc);
2298 ndis_sched((void(*)(void *))ndis_reset_nic, sc, NDIS_TASKQUEUE);
2299 ndis_sched(ndis_starttask, ifp, NDIS_TASKQUEUE);
2301 return;
2305 * Stop the adapter and free any mbufs allocated to the
2306 * RX and TX lists.
2308 static void
2309 ndis_stop(struct ndis_softc *sc)
2311 struct ifnet *ifp;
2312 int s;
2314 ifp = &sc->arpcom.ac_if;
2315 callout_stop(&sc->ndis_stat_ch);
2317 ndis_halt_nic(sc);
2319 NDIS_LOCK(sc);
2321 ifp->if_timer = 0;
2322 sc->ndis_link = 0;
2323 ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
2325 NDIS_UNLOCK(sc);
2327 return;
2331 * Stop all chip I/O so that the kernel's probe routines don't
2332 * get confused by errant DMAs when rebooting.
2334 /* TODO: remove this #ifdef once ndis_shutdown_nic() is working on NetBSD */