1 3147 xc151355 /* 2 10266 Quaker * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 3147 xc151355 * Use is subject to license terms. 4 3147 xc151355 */ 5 3147 xc151355 6 3147 xc151355 /* 7 3147 xc151355 * Copyright (c) 2001 Atsushi Onoe 8 3147 xc151355 * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting 9 3147 xc151355 * All rights reserved. 10 3147 xc151355 * 11 3147 xc151355 * Redistribution and use in source and binary forms, with or without 12 3147 xc151355 * modification, are permitted provided that the following conditions 13 3147 xc151355 * are met: 14 3147 xc151355 * 1. Redistributions of source code must retain the above copyright 15 3147 xc151355 * notice, this list of conditions and the following disclaimer. 16 3147 xc151355 * 2. Redistributions in binary form must reproduce the above copyright 17 3147 xc151355 * notice, this list of conditions and the following disclaimer in the 18 3147 xc151355 * documentation and/or other materials provided with the distribution. 19 3147 xc151355 * 3. The name of the author may not be used to endorse or promote products 20 3147 xc151355 * derived from this software without specific prior written permission. 21 3147 xc151355 * 22 3147 xc151355 * Alternatively, this software may be distributed under the terms of the 23 3147 xc151355 * GNU General Public License ("GPL") version 2 as published by the Free 24 3147 xc151355 * Software Foundation. 25 3147 xc151355 * 26 3147 xc151355 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27 3147 xc151355 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28 3147 xc151355 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29 3147 xc151355 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30 3147 xc151355 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31 3147 xc151355 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 3147 xc151355 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 3147 xc151355 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 3147 xc151355 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35 3147 xc151355 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 3147 xc151355 */ 37 3147 xc151355 38 3147 xc151355 /* 39 3147 xc151355 * IEEE 802.11 generic handler 40 3147 xc151355 */ 41 3147 xc151355 42 3147 xc151355 #include <sys/param.h> 43 3147 xc151355 #include <sys/types.h> 44 3147 xc151355 #include <sys/cmn_err.h> 45 3147 xc151355 #include <sys/modctl.h> 46 4126 zf162725 #include <sys/stropts.h> 47 4126 zf162725 #include <sys/door.h> 48 8275 Eric #include <sys/mac_provider.h> 49 3147 xc151355 #include "net80211_impl.h" 50 3147 xc151355 51 3147 xc151355 uint32_t ieee80211_debug = 0x0; /* debug msg flags */ 52 3147 xc151355 53 3147 xc151355 const char *ieee80211_phymode_name[] = { 54 3147 xc151355 "auto", /* IEEE80211_MODE_AUTO */ 55 3147 xc151355 "11a", /* IEEE80211_MODE_11A */ 56 3147 xc151355 "11b", /* IEEE80211_MODE_11B */ 57 3147 xc151355 "11g", /* IEEE80211_MODE_11G */ 58 3147 xc151355 "FH", /* IEEE80211_MODE_FH */ 59 3147 xc151355 "turboA", /* IEEE80211_MODE_TURBO_A */ 60 3147 xc151355 "turboG", /* IEEE80211_MODE_TURBO_G */ 61 10266 Quaker "sturboA", /* IEEE80211_MODE_STURBO_A */ 62 10266 Quaker "11na", /* IEEE80211_MODE_11NA */ 63 10266 Quaker "11ng", /* IEEE80211_MODE_11NG */ 64 3147 xc151355 }; 65 3147 xc151355 66 3147 xc151355 #define IEEE80211_DPRINT(_level, _fmt) do { \ 67 3147 xc151355 _NOTE(CONSTCOND) \ 68 3147 xc151355 va_list ap; \ 69 3147 xc151355 va_start(ap, (_fmt)); \ 70 3147 xc151355 vcmn_err((_level), (_fmt), ap); \ 71 3147 xc151355 va_end(ap); \ 72 3147 xc151355 _NOTE(CONSTCOND) \ 73 3147 xc151355 } while (0) 74 3147 xc151355 75 3147 xc151355 /* 76 3147 xc151355 * Print error messages 77 3147 xc151355 */ 78 3147 xc151355 void 79 3147 xc151355 ieee80211_err(const int8_t *fmt, ...) 80 3147 xc151355 { 81 3147 xc151355 IEEE80211_DPRINT(CE_WARN, fmt); 82 3147 xc151355 } 83 3147 xc151355 84 3147 xc151355 /* 85 3147 xc151355 * Print debug messages 86 3147 xc151355 */ 87 3147 xc151355 void 88 3147 xc151355 ieee80211_dbg(uint32_t flag, const int8_t *fmt, ...) 89 3147 xc151355 { 90 3147 xc151355 if (flag & ieee80211_debug) 91 3147 xc151355 IEEE80211_DPRINT(CE_CONT, fmt); 92 4126 zf162725 } 93 4126 zf162725 94 4126 zf162725 /* 95 4126 zf162725 * Alloc memory, and save the size 96 4126 zf162725 */ 97 4126 zf162725 void * 98 4126 zf162725 ieee80211_malloc(size_t size) 99 4126 zf162725 { 100 4126 zf162725 void *p = kmem_zalloc((size + 4), KM_SLEEP); 101 4126 zf162725 *(int *)p = size; 102 4126 zf162725 p = (char *)p + 4; 103 4126 zf162725 104 4126 zf162725 return (p); 105 4126 zf162725 } 106 4126 zf162725 107 4126 zf162725 void 108 4126 zf162725 ieee80211_free(void *p) 109 4126 zf162725 { 110 4126 zf162725 void *tp = (char *)p - 4; 111 4126 zf162725 kmem_free((char *)p - 4, *(int *)tp + 4); 112 4126 zf162725 } 113 4126 zf162725 114 4126 zf162725 void 115 4126 zf162725 ieee80211_mac_update(ieee80211com_t *ic) 116 4126 zf162725 { 117 4126 zf162725 wifi_data_t wd = { 0 }; 118 4126 zf162725 ieee80211_node_t *in; 119 4126 zf162725 120 4126 zf162725 /* 121 4126 zf162725 * We can send data now; update the fastpath with our 122 4126 zf162725 * current associated BSSID and other relevant settings. 123 4126 zf162725 */ 124 4126 zf162725 in = ic->ic_bss; 125 4126 zf162725 wd.wd_secalloc = ieee80211_crypto_getciphertype(ic); 126 4126 zf162725 wd.wd_opmode = ic->ic_opmode; 127 4126 zf162725 IEEE80211_ADDR_COPY(wd.wd_bssid, in->in_bssid); 128 10266 Quaker wd.wd_qospad = 0; 129 10266 Quaker if (in->in_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) { 130 10266 Quaker wd.wd_qospad = 2; 131 10266 Quaker if (ic->ic_flags & IEEE80211_F_DATAPAD) 132 10266 Quaker wd.wd_qospad = roundup(wd.wd_qospad, sizeof (uint32_t)); 133 10266 Quaker } 134 4126 zf162725 (void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd)); 135 4126 zf162725 mac_tx_update(ic->ic_mach); 136 4126 zf162725 ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_mac_update" 137 4126 zf162725 "(cipher = %d)\n", wd.wd_secalloc); 138 4126 zf162725 } 139 4126 zf162725 140 4126 zf162725 /* 141 4126 zf162725 * ieee80211_event_thread 142 4126 zf162725 * open door of wpa, send event to wpad service 143 4126 zf162725 */ 144 4126 zf162725 static void 145 4126 zf162725 ieee80211_event_thread(void *arg) 146 4126 zf162725 { 147 4126 zf162725 ieee80211com_t *ic = arg; 148 4126 zf162725 door_handle_t event_door = NULL; /* Door for upcalls */ 149 4126 zf162725 wl_events_t ev; 150 4126 zf162725 door_arg_t darg; 151 4126 zf162725 152 4126 zf162725 mutex_enter(&ic->ic_doorlock); 153 4126 zf162725 154 4126 zf162725 ev.event = ic->ic_eventq[ic->ic_evq_head]; 155 4126 zf162725 ic->ic_evq_head ++; 156 4126 zf162725 if (ic->ic_evq_head >= MAX_EVENT) 157 4126 zf162725 ic->ic_evq_head = 0; 158 4126 zf162725 159 4126 zf162725 ieee80211_dbg(IEEE80211_MSG_DEBUG, "ieee80211_event(%d)\n", ev.event); 160 4126 zf162725 /* 161 4126 zf162725 * Locate the door used for upcalls 162 4126 zf162725 */ 163 4126 zf162725 if (door_ki_open(ic->ic_wpadoor, &event_door) != 0) { 164 4126 zf162725 ieee80211_err("ieee80211_event: door_ki_open(%s) failed\n", 165 4126 zf162725 ic->ic_wpadoor); 166 4126 zf162725 goto out; 167 4126 zf162725 } 168 4126 zf162725 169 4126 zf162725 darg.data_ptr = (char *)&ev; 170 4126 zf162725 darg.data_size = sizeof (wl_events_t); 171 4126 zf162725 darg.desc_ptr = NULL; 172 4126 zf162725 darg.desc_num = 0; 173 4126 zf162725 darg.rbuf = NULL; 174 4126 zf162725 darg.rsize = 0; 175 4126 zf162725 176 6997 jwadams if (door_ki_upcall_limited(event_door, &darg, NULL, SIZE_MAX, 0) != 0) { 177 4126 zf162725 ieee80211_err("ieee80211_event: door_ki_upcall() failed\n"); 178 4126 zf162725 } 179 4126 zf162725 180 4126 zf162725 if (event_door) { /* release our hold (if any) */ 181 4126 zf162725 door_ki_rele(event_door); 182 4126 zf162725 } 183 4126 zf162725 184 4126 zf162725 out: 185 4126 zf162725 mutex_exit(&ic->ic_doorlock); 186 4126 zf162725 } 187 4126 zf162725 188 4126 zf162725 /* 189 4126 zf162725 * Notify state transition event message to WPA daemon 190 4126 zf162725 */ 191 4126 zf162725 void 192 4126 zf162725 ieee80211_notify(ieee80211com_t *ic, wpa_event_type event) 193 4126 zf162725 { 194 4126 zf162725 if ((ic->ic_flags & IEEE80211_F_WPA) == 0) 195 4126 zf162725 return; /* Not running on WPA mode */ 196 4126 zf162725 197 4126 zf162725 ic->ic_eventq[ic->ic_evq_tail] = event; 198 4126 zf162725 ic->ic_evq_tail ++; 199 4126 zf162725 if (ic->ic_evq_tail >= MAX_EVENT) ic->ic_evq_tail = 0; 200 4126 zf162725 201 4126 zf162725 /* async */ 202 4126 zf162725 (void) timeout(ieee80211_event_thread, (void *)ic, 0); 203 3147 xc151355 } 204 3147 xc151355 205 3147 xc151355 /* 206 5296 zf162725 * Register WPA door 207 5296 zf162725 */ 208 5296 zf162725 void 209 5296 zf162725 ieee80211_register_door(ieee80211com_t *ic, const char *drvname, int inst) 210 5296 zf162725 { 211 5296 zf162725 (void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", 212 5296 zf162725 WPA_DOOR, drvname, inst); 213 5296 zf162725 } 214 5296 zf162725 215 5296 zf162725 /* 216 3147 xc151355 * Default reset method for use with the ioctl support. This 217 3147 xc151355 * method is invoked after any state change in the 802.11 218 3147 xc151355 * layer that should be propagated to the hardware but not 219 3147 xc151355 * require re-initialization of the 802.11 state machine (e.g 220 3147 xc151355 * rescanning for an ap). We always return ENETRESET which 221 3147 xc151355 * should cause the driver to re-initialize the device. Drivers 222 3147 xc151355 * can override this method to implement more optimized support. 223 3147 xc151355 */ 224 3147 xc151355 /* ARGSUSED */ 225 3147 xc151355 static int 226 3147 xc151355 ieee80211_default_reset(ieee80211com_t *ic) 227 3147 xc151355 { 228 3147 xc151355 return (ENETRESET); 229 3147 xc151355 } 230 3147 xc151355 231 3147 xc151355 /* 232 3147 xc151355 * Convert channel to IEEE channel number. 233 3147 xc151355 */ 234 3147 xc151355 uint32_t 235 3147 xc151355 ieee80211_chan2ieee(ieee80211com_t *ic, struct ieee80211_channel *ch) 236 3147 xc151355 { 237 3147 xc151355 if ((ic->ic_sup_channels <= ch) && 238 3147 xc151355 (ch <= &ic->ic_sup_channels[IEEE80211_CHAN_MAX])) { 239 3147 xc151355 return (ch - ic->ic_sup_channels); 240 3147 xc151355 } else if (ch == IEEE80211_CHAN_ANYC) { 241 3147 xc151355 return (IEEE80211_CHAN_ANY); 242 3147 xc151355 } else if (ch != NULL) { 243 3147 xc151355 ieee80211_err("invalid channel freq %u flags %x\n", 244 5296 zf162725 ch->ich_freq, ch->ich_flags); 245 3147 xc151355 return (0); 246 3147 xc151355 } 247 3147 xc151355 ieee80211_err("invalid channel (NULL)\n"); /* ch == NULL */ 248 3147 xc151355 return (0); 249 3147 xc151355 } 250 3147 xc151355 251 3147 xc151355 /* 252 3147 xc151355 * Convert IEEE channel number to MHz frequency. 253 3147 xc151355 * chan IEEE channel number 254 3147 xc151355 * flags specify whether the frequency is in the 2GHz ISM 255 3147 xc151355 * band or the 5GHz band 256 3147 xc151355 * 257 3147 xc151355 * 802.11b 2GHz: 14 channels, each 5 MHz wide. Channel 1 is placed 258 3147 xc151355 * at 2.412 GHz, channel 2 at 2.417 GHz, and so on up to channel 13 259 3147 xc151355 * at 2.472 GHz. Channel 14 was defined especially for operation in 260 3147 xc151355 * Japan, and has a center frequency 2.484 GHz. 261 3147 xc151355 * 802.11g 2GHz: adopts the frequency plan of 802.11b. Japan only 262 3147 xc151355 * allows 802.11g operation in channels 1-13 263 3147 xc151355 * 802.11a 5GHz: starting every 5 MHz 264 3147 xc151355 * 802.11b/g channels 15-24 (2512-2692) are used by some implementation 265 3147 xc151355 * (Atheros etc.) 266 3147 xc151355 */ 267 3147 xc151355 uint32_t 268 3147 xc151355 ieee80211_ieee2mhz(uint32_t chan, uint32_t flags) 269 3147 xc151355 { 270 3147 xc151355 if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ 271 3147 xc151355 if (chan == 14) 272 3147 xc151355 return (2484); 273 3147 xc151355 if (chan < 14) 274 3147 xc151355 return (2412 + (chan - 1) * 5); 275 3147 xc151355 else 276 3147 xc151355 return (2512 + ((chan - 15) * 20)); 277 3147 xc151355 } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ 278 3147 xc151355 return (5000 + (chan * 5)); /* OFDM */ 279 3147 xc151355 } else { /* either, guess */ 280 3147 xc151355 if (chan == 14) 281 3147 xc151355 return (2484); 282 3147 xc151355 if (chan < 14) /* 0-13 */ 283 3147 xc151355 return (2412 + (chan - 1) * 5); 284 3147 xc151355 if (chan < 27) /* 15-26 */ 285 3147 xc151355 return (2512 + ((chan - 15) * 20)); 286 3147 xc151355 return (5000 + (chan * 5)); 287 3147 xc151355 } 288 3147 xc151355 } 289 3147 xc151355 290 3147 xc151355 /* 291 3147 xc151355 * Do late attach work. It must be called by the driver after 292 3147 xc151355 * calling ieee80211_attach() and before calling most ieee80211 293 3147 xc151355 * functions. 294 3147 xc151355 */ 295 3147 xc151355 void 296 3147 xc151355 ieee80211_media_init(ieee80211com_t *ic) 297 3147 xc151355 { 298 3147 xc151355 /* 299 3147 xc151355 * Do late attach work that must wait for any subclass 300 3147 xc151355 * (i.e. driver) work such as overriding methods. 301 3147 xc151355 */ 302 3147 xc151355 ieee80211_node_lateattach(ic); 303 3147 xc151355 } 304 3147 xc151355 305 3147 xc151355 /* 306 3147 xc151355 * Start Watchdog timer. After count down timer(s), ic_watchdog 307 3147 xc151355 * will be called 308 3147 xc151355 */ 309 3147 xc151355 void 310 3147 xc151355 ieee80211_start_watchdog(ieee80211com_t *ic, uint32_t timer) 311 3147 xc151355 { 312 3147 xc151355 if (ic->ic_watchdog_timer == 0 && ic->ic_watchdog != NULL) { 313 3147 xc151355 ic->ic_watchdog_timer = timeout(ic->ic_watchdog, ic, 314 5296 zf162725 drv_usectohz(1000000 * timer)); 315 3147 xc151355 } 316 3147 xc151355 } 317 3147 xc151355 318 3147 xc151355 /* 319 3147 xc151355 * Stop watchdog timer. 320 3147 xc151355 */ 321 3147 xc151355 void 322 3147 xc151355 ieee80211_stop_watchdog(ieee80211com_t *ic) 323 3147 xc151355 { 324 3147 xc151355 if (ic->ic_watchdog_timer != 0) { 325 3147 xc151355 if (ic->ic_watchdog != NULL) 326 3147 xc151355 (void) untimeout(ic->ic_watchdog_timer); 327 3147 xc151355 ic->ic_watchdog_timer = 0; 328 3147 xc151355 } 329 3147 xc151355 } 330 3147 xc151355 331 3147 xc151355 /* 332 3147 xc151355 * Called from a driver's xxx_watchdog routine. It is used to 333 3147 xc151355 * perform periodic cleanup of state for net80211, as well as 334 3147 xc151355 * timeout scans. 335 3147 xc151355 */ 336 3147 xc151355 void 337 3147 xc151355 ieee80211_watchdog(void *arg) 338 3147 xc151355 { 339 3147 xc151355 ieee80211com_t *ic = arg; 340 3147 xc151355 struct ieee80211_impl *im = ic->ic_private; 341 3147 xc151355 ieee80211_node_table_t *nt; 342 3147 xc151355 int inact_timer = 0; 343 3147 xc151355 344 3147 xc151355 if (ic->ic_state == IEEE80211_S_INIT) 345 3147 xc151355 return; 346 3147 xc151355 347 3147 xc151355 IEEE80211_LOCK(ic); 348 3147 xc151355 if ((im->im_mgt_timer != 0) && (--im->im_mgt_timer == 0)) { 349 3147 xc151355 IEEE80211_UNLOCK(ic); 350 3147 xc151355 ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 351 3147 xc151355 IEEE80211_LOCK(ic); 352 3147 xc151355 } 353 3147 xc151355 354 3147 xc151355 nt = &ic->ic_scan; 355 3147 xc151355 if (nt->nt_inact_timer != 0) { 356 3147 xc151355 if (--nt->nt_inact_timer == 0) 357 3147 xc151355 nt->nt_timeout(nt); 358 3147 xc151355 inact_timer += nt->nt_inact_timer; 359 3147 xc151355 } 360 3147 xc151355 nt = &ic->ic_sta; 361 3147 xc151355 if (nt->nt_inact_timer != 0) { 362 3147 xc151355 if (--nt->nt_inact_timer == 0) 363 3147 xc151355 nt->nt_timeout(nt); 364 3147 xc151355 inact_timer += nt->nt_inact_timer; 365 3147 xc151355 } 366 3147 xc151355 367 3147 xc151355 IEEE80211_UNLOCK(ic); 368 3147 xc151355 369 3147 xc151355 if (im->im_mgt_timer != 0 || inact_timer > 0) 370 3147 xc151355 ieee80211_start_watchdog(ic, 1); 371 3147 xc151355 } 372 3147 xc151355 373 3147 xc151355 /* 374 3147 xc151355 * Set the current phy mode and recalculate the active channel 375 3147 xc151355 * set and supported rates based on the available channels for 376 3147 xc151355 * this mode. Also select a new BSS channel if the current one 377 3147 xc151355 * is inappropriate for this mode. 378 3147 xc151355 * This function is called by net80211, and not intended to be 379 3147 xc151355 * called directly. 380 3147 xc151355 */ 381 3147 xc151355 static int 382 3147 xc151355 ieee80211_setmode(ieee80211com_t *ic, enum ieee80211_phymode mode) 383 3147 xc151355 { 384 3147 xc151355 static const uint32_t chanflags[] = { 385 3147 xc151355 0, /* IEEE80211_MODE_AUTO */ 386 3147 xc151355 IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ 387 3147 xc151355 IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ 388 3147 xc151355 IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ 389 3147 xc151355 IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ 390 3147 xc151355 IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */ 391 3147 xc151355 IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */ 392 10266 Quaker IEEE80211_CHAN_ST, /* IEEE80211_MODE_STURBO_A */ 393 10266 Quaker IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA (check legacy) */ 394 10266 Quaker IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG (check legacy) */ 395 3147 xc151355 }; 396 3147 xc151355 struct ieee80211_channel *ch; 397 3147 xc151355 uint32_t modeflags; 398 3147 xc151355 int i; 399 3147 xc151355 int achannels = 0; 400 3147 xc151355 401 3147 xc151355 /* validate new mode */ 402 3147 xc151355 if ((ic->ic_modecaps & (1 << mode)) == 0) { 403 3147 xc151355 ieee80211_err("ieee80211_setmode(): mode %u not supported" 404 5296 zf162725 " (caps 0x%x)\n", mode, ic->ic_modecaps); 405 3147 xc151355 return (EINVAL); 406 3147 xc151355 } 407 3147 xc151355 408 3147 xc151355 /* 409 3147 xc151355 * Verify at least one channel is present in the available 410 3147 xc151355 * channel list before committing to the new mode. 411 3147 xc151355 * Calculate the active channel set. 412 3147 xc151355 */ 413 3147 xc151355 ASSERT(mode < IEEE80211_N(chanflags)); 414 3147 xc151355 modeflags = chanflags[mode]; 415 3147 xc151355 bzero(ic->ic_chan_active, sizeof (ic->ic_chan_active)); 416 3147 xc151355 for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 417 3147 xc151355 ch = &ic->ic_sup_channels[i]; 418 3147 xc151355 if (ch->ich_flags == 0) 419 3147 xc151355 continue; 420 3147 xc151355 if (mode == IEEE80211_MODE_AUTO) { 421 3147 xc151355 /* take anything but pure turbo channels */ 422 3147 xc151355 if ((ch->ich_flags & ~IEEE80211_CHAN_TURBO) != 0) { 423 3147 xc151355 ieee80211_setbit(ic->ic_chan_active, i); 424 3147 xc151355 achannels++; 425 3147 xc151355 } 426 3147 xc151355 } else { 427 3147 xc151355 if ((ch->ich_flags & modeflags) == modeflags) { 428 3147 xc151355 ieee80211_setbit(ic->ic_chan_active, i); 429 3147 xc151355 achannels++; 430 3147 xc151355 } 431 3147 xc151355 } 432 3147 xc151355 } 433 3147 xc151355 if (achannels == 0) { 434 3147 xc151355 ieee80211_err("ieee80211_setmode(): " 435 5296 zf162725 "no channel found for mode %u\n", mode); 436 3147 xc151355 return (EINVAL); 437 3147 xc151355 } 438 3147 xc151355 439 3147 xc151355 /* 440 3147 xc151355 * If no current/default channel is setup or the current 441 3147 xc151355 * channel is wrong for the mode then pick the first 442 3147 xc151355 * available channel from the active list. This is likely 443 3147 xc151355 * not the right one. 444 3147 xc151355 */ 445 3147 xc151355 if (ic->ic_ibss_chan == NULL || 446 3147 xc151355 ieee80211_isclr(ic->ic_chan_active, 447 3147 xc151355 ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 448 3147 xc151355 for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 449 3147 xc151355 if (ieee80211_isset(ic->ic_chan_active, i)) { 450 3147 xc151355 ic->ic_ibss_chan = &ic->ic_sup_channels[i]; 451 3147 xc151355 break; 452 3147 xc151355 } 453 3147 xc151355 } 454 3147 xc151355 } 455 3147 xc151355 /* 456 3147 xc151355 * If the desired channel is set but no longer valid then reset it. 457 3147 xc151355 */ 458 3147 xc151355 if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 459 3147 xc151355 ieee80211_isclr(ic->ic_chan_active, 460 3147 xc151355 ieee80211_chan2ieee(ic, ic->ic_des_chan))) { 461 3147 xc151355 ic->ic_des_chan = IEEE80211_CHAN_ANYC; 462 3147 xc151355 } 463 3147 xc151355 464 3147 xc151355 /* 465 3147 xc151355 * Do mode-specific rate setup. 466 3147 xc151355 */ 467 3147 xc151355 if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) 468 3147 xc151355 ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); 469 3147 xc151355 470 3147 xc151355 /* 471 3147 xc151355 * Setup an initial rate set according to the 472 3147 xc151355 * current/default channel. This will be changed 473 3147 xc151355 * when scanning but must exist now so drivers have 474 3147 xc151355 * consistent state of ic_bsschan. 475 3147 xc151355 */ 476 3147 xc151355 if (ic->ic_bss != NULL) 477 3147 xc151355 ic->ic_bss->in_rates = ic->ic_sup_rates[mode]; 478 3147 xc151355 ic->ic_curmode = mode; 479 3147 xc151355 ieee80211_reset_erp(ic); /* reset ERP state */ 480 10266 Quaker ieee80211_wme_initparams(ic); /* reset WME stat */ 481 3147 xc151355 482 3147 xc151355 return (0); 483 3147 xc151355 } 484 3147 xc151355 485 3147 xc151355 /* 486 3147 xc151355 * Return the phy mode for with the specified channel so the 487 3147 xc151355 * caller can select a rate set. This is problematic for channels 488 3147 xc151355 * where multiple operating modes are possible (e.g. 11g+11b). 489 3147 xc151355 * In those cases we defer to the current operating mode when set. 490 3147 xc151355 */ 491 10266 Quaker /* ARGSUSED */ 492 3147 xc151355 enum ieee80211_phymode 493 3147 xc151355 ieee80211_chan2mode(ieee80211com_t *ic, struct ieee80211_channel *chan) 494 3147 xc151355 { 495 10266 Quaker if (IEEE80211_IS_CHAN_HTA(chan)) 496 10266 Quaker return (IEEE80211_MODE_11NA); 497 10266 Quaker else if (IEEE80211_IS_CHAN_HTG(chan)) 498 10266 Quaker return (IEEE80211_MODE_11NG); 499 10266 Quaker else if (IEEE80211_IS_CHAN_108G(chan)) 500 10266 Quaker return (IEEE80211_MODE_TURBO_G); 501 10266 Quaker else if (IEEE80211_IS_CHAN_ST(chan)) 502 10266 Quaker return (IEEE80211_MODE_STURBO_A); 503 10266 Quaker else if (IEEE80211_IS_CHAN_T(chan)) 504 3147 xc151355 return (IEEE80211_MODE_TURBO_A); 505 10266 Quaker else if (IEEE80211_IS_CHAN_A(chan)) 506 3147 xc151355 return (IEEE80211_MODE_11A); 507 10266 Quaker else if (IEEE80211_IS_CHAN_ANYG(chan)) 508 10266 Quaker return (IEEE80211_MODE_11G); 509 10266 Quaker else if (IEEE80211_IS_CHAN_B(chan)) 510 10266 Quaker return (IEEE80211_MODE_11B); 511 10266 Quaker else if (IEEE80211_IS_CHAN_FHSS(chan)) 512 3147 xc151355 return (IEEE80211_MODE_FH); 513 10266 Quaker 514 10266 Quaker /* NB: should not get here */ 515 10266 Quaker ieee80211_err("cannot map channel to mode; freq %u flags 0x%x\n", 516 10266 Quaker chan->ich_freq, chan->ich_flags); 517 10266 Quaker 518 10266 Quaker return (IEEE80211_MODE_11B); 519 10266 Quaker } 520 10266 Quaker 521 10266 Quaker const struct ieee80211_rateset * 522 10266 Quaker ieee80211_get_suprates(ieee80211com_t *ic, struct ieee80211_channel *c) 523 10266 Quaker { 524 10266 Quaker if (IEEE80211_IS_CHAN_HTA(c)) 525 10266 Quaker return (&ic->ic_sup_rates[IEEE80211_MODE_11A]); 526 10266 Quaker if (IEEE80211_IS_CHAN_HTG(c)) { 527 10266 Quaker return (&ic->ic_sup_rates[IEEE80211_MODE_11G]); 528 3147 xc151355 } 529 10266 Quaker return (&ic->ic_sup_rates[ieee80211_chan2mode(ic, c)]); 530 10266 Quaker } 531 10266 Quaker 532 10266 Quaker /* 533 10266 Quaker * Locate a channel given a frequency+flags. We cache 534 10266 Quaker * the previous lookup to optimize swithing between two 535 10266 Quaker * channels--as happens with dynamic turbo. 536 10266 Quaker */ 537 10266 Quaker struct ieee80211_channel * 538 10266 Quaker ieee80211_find_channel(ieee80211com_t *ic, int freq, int flags) 539 10266 Quaker { 540 10266 Quaker struct ieee80211_channel *c; 541 10266 Quaker int i; 542 10266 Quaker 543 10266 Quaker flags &= IEEE80211_CHAN_ALLTURBO; 544 10266 Quaker /* brute force search */ 545 10266 Quaker for (i = 0; i < IEEE80211_CHAN_MAX; i++) { 546 10266 Quaker c = &ic->ic_sup_channels[i]; 547 10266 Quaker if (c->ich_freq == freq && 548 10266 Quaker (c->ich_flags & IEEE80211_CHAN_ALLTURBO) == flags) 549 10266 Quaker return (c); 550 10266 Quaker } 551 10266 Quaker return (NULL); 552 3147 xc151355 } 553 3147 xc151355 554 3147 xc151355 /* 555 3147 xc151355 * Return the size of the 802.11 header for a management or data frame. 556 3147 xc151355 */ 557 3147 xc151355 int 558 10266 Quaker ieee80211_hdrsize(const void *data) 559 3147 xc151355 { 560 3147 xc151355 const struct ieee80211_frame *wh = data; 561 3147 xc151355 int size = sizeof (struct ieee80211_frame); 562 3147 xc151355 563 3147 xc151355 /* NB: we don't handle control frames */ 564 3147 xc151355 ASSERT((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) != 565 5296 zf162725 IEEE80211_FC0_TYPE_CTL); 566 3147 xc151355 if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) 567 3147 xc151355 size += IEEE80211_ADDR_LEN; 568 10266 Quaker if (IEEE80211_QOS_HAS_SEQ(wh)) 569 10266 Quaker size += sizeof (uint16_t); 570 3147 xc151355 571 10266 Quaker return (size); 572 10266 Quaker } 573 10266 Quaker 574 10266 Quaker /* 575 10266 Quaker * Return the space occupied by the 802.11 header and any 576 10266 Quaker * padding required by the driver. This works for a 577 10266 Quaker * management or data frame. 578 10266 Quaker */ 579 10266 Quaker int 580 10266 Quaker ieee80211_hdrspace(ieee80211com_t *ic, const void *data) 581 10266 Quaker { 582 10266 Quaker int size = ieee80211_hdrsize(data); 583 10266 Quaker if (ic->ic_flags & IEEE80211_F_DATAPAD) 584 10266 Quaker size = roundup(size, sizeof (uint32_t)); 585 10266 Quaker return (size); 586 10266 Quaker } 587 10266 Quaker 588 10266 Quaker /* 589 10266 Quaker * Like ieee80211_hdrsize, but handles any type of frame. 590 10266 Quaker */ 591 10266 Quaker int 592 10266 Quaker ieee80211_anyhdrsize(const void *data) 593 10266 Quaker { 594 10266 Quaker const struct ieee80211_frame *wh = data; 595 10266 Quaker 596 10266 Quaker if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) { 597 10266 Quaker switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) { 598 10266 Quaker case IEEE80211_FC0_SUBTYPE_CTS: 599 10266 Quaker case IEEE80211_FC0_SUBTYPE_ACK: 600 10266 Quaker return (sizeof (struct ieee80211_frame_ack)); 601 10266 Quaker case IEEE80211_FC0_SUBTYPE_BAR: 602 10266 Quaker return (sizeof (struct ieee80211_frame_bar)); 603 10266 Quaker } 604 10266 Quaker return (sizeof (struct ieee80211_frame_min)); 605 10266 Quaker } else 606 10266 Quaker return (ieee80211_hdrsize(data)); 607 10266 Quaker } 608 10266 Quaker 609 10266 Quaker /* 610 10266 Quaker * Like ieee80211_hdrspace, but handles any type of frame. 611 10266 Quaker */ 612 10266 Quaker int 613 10266 Quaker ieee80211_anyhdrspace(ieee80211com_t *ic, const void *data) 614 10266 Quaker { 615 10266 Quaker int size = ieee80211_anyhdrsize(data); 616 10266 Quaker if (ic->ic_flags & IEEE80211_F_DATAPAD) 617 10266 Quaker size = roundup(size, sizeof (uint32_t)); 618 3147 xc151355 return (size); 619 3147 xc151355 } 620 3147 xc151355 621 3147 xc151355 /* 622 3147 xc151355 * Allocate and setup a management frame of the specified 623 3147 xc151355 * size. We return the mblk and a pointer to the start 624 3147 xc151355 * of the contiguous data area that's been reserved based 625 3147 xc151355 * on the packet length. 626 3147 xc151355 */ 627 3147 xc151355 mblk_t * 628 3147 xc151355 ieee80211_getmgtframe(uint8_t **frm, int pktlen) 629 3147 xc151355 { 630 3147 xc151355 mblk_t *mp; 631 3147 xc151355 int len; 632 3147 xc151355 633 3147 xc151355 len = sizeof (struct ieee80211_frame) + pktlen; 634 3147 xc151355 mp = allocb(len, BPRI_MED); 635 3147 xc151355 if (mp != NULL) { 636 3147 xc151355 *frm = mp->b_rptr + sizeof (struct ieee80211_frame); 637 3147 xc151355 mp->b_wptr = mp->b_rptr + len; 638 3147 xc151355 } else { 639 3147 xc151355 ieee80211_err("ieee80211_getmgtframe: " 640 5296 zf162725 "alloc frame failed, %d\n", len); 641 3147 xc151355 } 642 3147 xc151355 return (mp); 643 3147 xc151355 } 644 3147 xc151355 645 3147 xc151355 /* 646 3147 xc151355 * Send system messages to notify the device has joined a WLAN. 647 3147 xc151355 * This is an OS specific function. Solaris marks link status 648 3147 xc151355 * as up. 649 3147 xc151355 */ 650 3147 xc151355 void 651 3147 xc151355 ieee80211_notify_node_join(ieee80211com_t *ic, ieee80211_node_t *in) 652 3147 xc151355 { 653 3147 xc151355 if (in == ic->ic_bss) 654 3147 xc151355 mac_link_update(ic->ic_mach, LINK_STATE_UP); 655 4126 zf162725 ieee80211_notify(ic, EVENT_ASSOC); /* notify WPA service */ 656 3147 xc151355 } 657 3147 xc151355 658 3147 xc151355 /* 659 3147 xc151355 * Send system messages to notify the device has left a WLAN. 660 3147 xc151355 * This is an OS specific function. Solaris marks link status 661 3147 xc151355 * as down. 662 3147 xc151355 */ 663 3147 xc151355 void 664 3147 xc151355 ieee80211_notify_node_leave(ieee80211com_t *ic, ieee80211_node_t *in) 665 3147 xc151355 { 666 3147 xc151355 if (in == ic->ic_bss) 667 3147 xc151355 mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 668 4126 zf162725 ieee80211_notify(ic, EVENT_DISASSOC); /* notify WPA service */ 669 3147 xc151355 } 670 10266 Quaker 671 3147 xc151355 672 3147 xc151355 /* 673 3147 xc151355 * Get 802.11 kstats defined in ieee802.11(5) 674 3147 xc151355 * 675 3147 xc151355 * Return 0 on success 676 3147 xc151355 */ 677 3147 xc151355 int 678 3147 xc151355 ieee80211_stat(ieee80211com_t *ic, uint_t stat, uint64_t *val) 679 3147 xc151355 { 680 3147 xc151355 ASSERT(val != NULL); 681 3147 xc151355 IEEE80211_LOCK(ic); 682 3147 xc151355 switch (stat) { 683 3147 xc151355 case WIFI_STAT_TX_FRAGS: 684 3147 xc151355 *val = ic->ic_stats.is_tx_frags; 685 3147 xc151355 break; 686 3147 xc151355 case WIFI_STAT_MCAST_TX: 687 3147 xc151355 *val = ic->ic_stats.is_tx_mcast; 688 3147 xc151355 break; 689 3147 xc151355 case WIFI_STAT_TX_FAILED: 690 3147 xc151355 *val = ic->ic_stats.is_tx_failed; 691 3147 xc151355 break; 692 3147 xc151355 case WIFI_STAT_TX_RETRANS: 693 3147 xc151355 *val = ic->ic_stats.is_tx_retries; 694 3147 xc151355 break; 695 3147 xc151355 case WIFI_STAT_RTS_SUCCESS: 696 3147 xc151355 *val = ic->ic_stats.is_rts_success; 697 3147 xc151355 break; 698 3147 xc151355 case WIFI_STAT_RTS_FAILURE: 699 3147 xc151355 *val = ic->ic_stats.is_rts_failure; 700 3147 xc151355 break; 701 3147 xc151355 case WIFI_STAT_ACK_FAILURE: 702 3147 xc151355 *val = ic->ic_stats.is_ack_failure; 703 3147 xc151355 break; 704 3147 xc151355 case WIFI_STAT_RX_FRAGS: 705 3147 xc151355 *val = ic->ic_stats.is_rx_frags; 706 3147 xc151355 break; 707 3147 xc151355 case WIFI_STAT_MCAST_RX: 708 3147 xc151355 *val = ic->ic_stats.is_rx_mcast; 709 3147 xc151355 break; 710 3147 xc151355 case WIFI_STAT_RX_DUPS: 711 3147 xc151355 *val = ic->ic_stats.is_rx_dups; 712 3147 xc151355 break; 713 3147 xc151355 case WIFI_STAT_FCS_ERRORS: 714 3147 xc151355 *val = ic->ic_stats.is_fcs_errors; 715 3147 xc151355 break; 716 3147 xc151355 case WIFI_STAT_WEP_ERRORS: 717 3147 xc151355 *val = ic->ic_stats.is_wep_errors; 718 3147 xc151355 break; 719 3147 xc151355 } 720 3147 xc151355 IEEE80211_UNLOCK(ic); 721 3147 xc151355 return (0); 722 3147 xc151355 } 723 3147 xc151355 724 3147 xc151355 /* 725 3147 xc151355 * Attach network interface to the 802.11 support module. This 726 3147 xc151355 * function must be called before using any of the ieee80211 727 3147 xc151355 * functionss. The parameter "ic" MUST be initialized to tell 728 3147 xc151355 * net80211 about interface's capabilities. 729 3147 xc151355 */ 730 3147 xc151355 void 731 3147 xc151355 ieee80211_attach(ieee80211com_t *ic) 732 3147 xc151355 { 733 3147 xc151355 struct ieee80211_impl *im; 734 3147 xc151355 struct ieee80211_channel *ch; 735 3147 xc151355 int i; 736 3147 xc151355 737 3147 xc151355 /* Check mandatory callback functions not NULL */ 738 3147 xc151355 ASSERT(ic->ic_xmit != NULL); 739 3147 xc151355 740 3147 xc151355 mutex_init(&ic->ic_genlock, NULL, MUTEX_DRIVER, NULL); 741 4126 zf162725 mutex_init(&ic->ic_doorlock, NULL, MUTEX_DRIVER, NULL); 742 3147 xc151355 743 3147 xc151355 im = kmem_alloc(sizeof (ieee80211_impl_t), KM_SLEEP); 744 3147 xc151355 ic->ic_private = im; 745 3147 xc151355 cv_init(&im->im_scan_cv, NULL, CV_DRIVER, NULL); 746 3147 xc151355 747 3147 xc151355 /* 748 3147 xc151355 * Fill in 802.11 available channel set, mark 749 3147 xc151355 * all available channels as active, and pick 750 3147 xc151355 * a default channel if not already specified. 751 3147 xc151355 */ 752 3147 xc151355 bzero(im->im_chan_avail, sizeof (im->im_chan_avail)); 753 3147 xc151355 ic->ic_modecaps |= 1 << IEEE80211_MODE_AUTO; 754 3147 xc151355 for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 755 3147 xc151355 ch = &ic->ic_sup_channels[i]; 756 3147 xc151355 if (ch->ich_flags) { 757 3147 xc151355 /* Verify driver passed us valid data */ 758 3147 xc151355 if (i != ieee80211_chan2ieee(ic, ch)) { 759 3147 xc151355 ieee80211_err("bad channel ignored: " 760 5296 zf162725 "freq %u flags%x number %u\n", 761 5296 zf162725 ch->ich_freq, ch->ich_flags, i); 762 3147 xc151355 ch->ich_flags = 0; 763 3147 xc151355 continue; 764 3147 xc151355 } 765 3147 xc151355 ieee80211_setbit(im->im_chan_avail, i); 766 3147 xc151355 /* Identify mode capabilities */ 767 3147 xc151355 if (IEEE80211_IS_CHAN_A(ch)) 768 3147 xc151355 ic->ic_modecaps |= 1 << IEEE80211_MODE_11A; 769 3147 xc151355 if (IEEE80211_IS_CHAN_B(ch)) 770 3147 xc151355 ic->ic_modecaps |= 1 << IEEE80211_MODE_11B; 771 3147 xc151355 if (IEEE80211_IS_CHAN_PUREG(ch)) 772 3147 xc151355 ic->ic_modecaps |= 1 << IEEE80211_MODE_11G; 773 3147 xc151355 if (IEEE80211_IS_CHAN_FHSS(ch)) 774 3147 xc151355 ic->ic_modecaps |= 1 << IEEE80211_MODE_FH; 775 3147 xc151355 if (IEEE80211_IS_CHAN_T(ch)) 776 3147 xc151355 ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_A; 777 3147 xc151355 if (IEEE80211_IS_CHAN_108G(ch)) 778 3147 xc151355 ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_G; 779 10266 Quaker if (IEEE80211_IS_CHAN_ST(ch)) 780 10266 Quaker ic->ic_modecaps |= 1 << IEEE80211_MODE_STURBO_A; 781 10266 Quaker if (IEEE80211_IS_CHAN_HTA(ch)) 782 10266 Quaker ic->ic_modecaps |= 1 << IEEE80211_MODE_11NA; 783 10266 Quaker if (IEEE80211_IS_CHAN_HTG(ch)) 784 10266 Quaker ic->ic_modecaps |= 1 << IEEE80211_MODE_11NG; 785 3147 xc151355 if (ic->ic_curchan == NULL) { 786 3147 xc151355 /* arbitrarily pick the first channel */ 787 3147 xc151355 ic->ic_curchan = &ic->ic_sup_channels[i]; 788 3147 xc151355 } 789 3147 xc151355 } 790 3147 xc151355 } 791 3147 xc151355 /* validate ic->ic_curmode */ 792 3147 xc151355 if ((ic->ic_modecaps & (1 << ic->ic_curmode)) == 0) 793 3147 xc151355 ic->ic_curmode = IEEE80211_MODE_AUTO; 794 3147 xc151355 ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ 795 3147 xc151355 (void) ieee80211_setmode(ic, ic->ic_curmode); 796 3147 xc151355 797 10266 Quaker if (ic->ic_caps & IEEE80211_C_WME) /* enable if capable */ 798 10266 Quaker ic->ic_flags |= IEEE80211_F_WME; 799 3147 xc151355 if (ic->ic_caps & IEEE80211_C_BURST) 800 3147 xc151355 ic->ic_flags |= IEEE80211_F_BURST; 801 3147 xc151355 ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; 802 3147 xc151355 ic->ic_lintval = ic->ic_bintval; 803 3147 xc151355 ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; 804 3147 xc151355 ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; 805 3147 xc151355 806 3147 xc151355 ic->ic_reset = ieee80211_default_reset; 807 3147 xc151355 808 3147 xc151355 ieee80211_node_attach(ic); 809 3147 xc151355 ieee80211_proto_attach(ic); 810 3147 xc151355 ieee80211_crypto_attach(ic); 811 10266 Quaker ieee80211_ht_attach(ic); 812 3147 xc151355 813 3147 xc151355 ic->ic_watchdog_timer = 0; 814 3147 xc151355 } 815 3147 xc151355 816 3147 xc151355 /* 817 3147 xc151355 * Free any ieee80211 structures associated with the driver. 818 3147 xc151355 */ 819 3147 xc151355 void 820 3147 xc151355 ieee80211_detach(ieee80211com_t *ic) 821 3147 xc151355 { 822 3147 xc151355 struct ieee80211_impl *im = ic->ic_private; 823 3147 xc151355 824 3147 xc151355 ieee80211_stop_watchdog(ic); 825 3147 xc151355 cv_destroy(&im->im_scan_cv); 826 3147 xc151355 kmem_free(im, sizeof (ieee80211_impl_t)); 827 3147 xc151355 828 6629 zf162725 if (ic->ic_opt_ie != NULL) 829 6629 zf162725 ieee80211_free(ic->ic_opt_ie); 830 6629 zf162725 831 10266 Quaker ieee80211_ht_detach(ic); 832 3147 xc151355 ieee80211_node_detach(ic); 833 3147 xc151355 ieee80211_crypto_detach(ic); 834 3147 xc151355 835 3147 xc151355 mutex_destroy(&ic->ic_genlock); 836 4126 zf162725 mutex_destroy(&ic->ic_doorlock); 837 3147 xc151355 } 838 3147 xc151355 839 3147 xc151355 static struct modlmisc i_wifi_modlmisc = { 840 3147 xc151355 &mod_miscops, 841 10266 Quaker "IEEE80211 Kernel Module v2.0" 842 3147 xc151355 }; 843 3147 xc151355 844 3147 xc151355 static struct modlinkage i_wifi_modlinkage = { 845 3147 xc151355 MODREV_1, 846 3147 xc151355 &i_wifi_modlmisc, 847 3147 xc151355 NULL 848 3147 xc151355 }; 849 3147 xc151355 850 3147 xc151355 /* 851 3147 xc151355 * modlinkage functions 852 3147 xc151355 */ 853 3147 xc151355 int 854 3147 xc151355 _init(void) 855 3147 xc151355 { 856 3147 xc151355 return (mod_install(&i_wifi_modlinkage)); 857 3147 xc151355 } 858 3147 xc151355 859 3147 xc151355 int 860 3147 xc151355 _fini(void) 861 3147 xc151355 { 862 3147 xc151355 return (mod_remove(&i_wifi_modlinkage)); 863 3147 xc151355 } 864 3147 xc151355 865 3147 xc151355 int 866 3147 xc151355 _info(struct modinfo *modinfop) 867 3147 xc151355 { 868 3147 xc151355 return (mod_info(&i_wifi_modlinkage, modinfop)); 869 3147 xc151355 } 870