xref: /illumos-gate/usr/src/uts/common/io/zyd/zyd.c (revision e8da18d8)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 2007 by  Lukas Turek <turek@ksvi.mff.cuni.cz>
8  * Copyright (c) 2007 by  Jiri Svoboda <jirik.svoboda@seznam.cz>
9  * Copyright (c) 2007 by  Martin Krulis <martin.krulis@matfyz.cz>
10  * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr>
11  * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de>
12  *
13  * Permission to use, copy, modify, and distribute this software for any
14  * purpose with or without fee is hereby granted, provided that the above
15  * copyright notice and this permission notice appear in all copies.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  *
25  */
26 
27 /*
28  * ZD1211 wLAN driver
29  * Driver major routines
30  */
31 
32 #include <sys/byteorder.h>
33 #include <sys/ddi.h>
34 #include <sys/sunddi.h>
35 #include <sys/conf.h>
36 #include <sys/modctl.h>
37 #include <sys/mac_provider.h>
38 #include <sys/mac_wifi.h>
39 #include <sys/strsun.h>
40 #include <sys/ksynch.h>
41 
42 #include "zyd.h"
43 #include "zyd_reg.h"
44 
45 static int zyd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
46 static int zyd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
47 
48 static int zyd_m_stat(void *arg, uint_t stat, uint64_t *val);
49 static int zyd_m_start(void *arg);
50 static void zyd_m_stop(void *arg);
51 static int zyd_m_unicst(void *arg, const uint8_t *macaddr);
52 static int zyd_m_multicst(void *arg, boolean_t add, const uint8_t *m);
53 static int zyd_m_promisc(void *arg, boolean_t on);
54 static void zyd_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
55 static mblk_t *zyd_m_tx(void *arg, mblk_t *mp);
56 static int zyd_m_getprop(void *arg, const char *pr_name,
57     mac_prop_id_t wldp_pr_num, uint_t pr_flags,
58     uint_t wldp_length, void *wldp_buf, uint_t *perm);
59 static int zyd_m_setprop(void *arg, const char *pr_name,
60     mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf);
61 
62 static int zyd_newstate(struct ieee80211com *ic,
63     enum ieee80211_state state, int arg);
64 
65 /* Driver identification */
66 static char zyd_ident[] = ZYD_DRV_DESC " " ZYD_DRV_REV;
67 
68 /* Global state pointer for managing per-device soft states */
69 void *zyd_ssp;
70 
71 /*
72  * Mac Call Back entries
73  */
74 static mac_callbacks_t zyd_m_callbacks = {
75 	MC_IOCTL | MC_SETPROP | MC_GETPROP,
76 	zyd_m_stat,		/* Get the value of a statistic */
77 	zyd_m_start,		/* Start the device */
78 	zyd_m_stop,		/* Stop the device */
79 	zyd_m_promisc,		/* Enable or disable promiscuous mode */
80 	zyd_m_multicst,		/* Enable or disable a multicast addr */
81 	zyd_m_unicst,		/* Set the unicast MAC address */
82 	zyd_m_tx,		/* Transmit a packet */
83 	zyd_m_ioctl,		/* Process an unknown ioctl */
84 	NULL,			/* mc_getcapab */
85 	NULL,
86 	NULL,
87 	zyd_m_setprop,
88 	zyd_m_getprop
89 };
90 
91 /*
92  *  Module Loading Data & Entry Points
93  */
94 DDI_DEFINE_STREAM_OPS(zyd_devops,	/* name */
95     nulldev,			/* identify */
96     nulldev,			/* probe */
97     zyd_attach,			/* attach */
98     zyd_detach,			/* detach */
99     nodev,			/* reset */
100     NULL,			/* getinfo */
101     D_MP,			/* flag */
102     NULL,			/* stream_tab */
103     ddi_quiesce_not_needed	/* quiesce */
104 );
105 
106 static struct modldrv zyd_modldrv = {
107 	&mod_driverops,		/* drv_modops */
108 	zyd_ident,		/* drv_linkinfo */
109 	&zyd_devops		/* drv_dev_ops */
110 };
111 
112 static struct modlinkage zyd_ml = {
113 	MODREV_1,		/* ml_rev */
114 	{&zyd_modldrv, NULL}	/* ml_linkage */
115 };
116 
117 /*
118  * Wireless-specific structures
119  */
120 static const struct ieee80211_rateset zyd_rateset_11b = {
121 	4, {2, 4, 11, 22}	/* units are 0.5Mbit! */
122 };
123 
124 static const struct ieee80211_rateset zyd_rateset_11g = {
125 	12, {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108}
126 };
127 
128 
129 #ifdef DEBUG
130 uint32_t zyd_dbg_flags;
131 
132 void
133 zyd_dbg(uint32_t dbg_mask, const int8_t *fmt, ...)
134 {
135 	va_list args;
136 
137 	if (dbg_mask & zyd_dbg_flags) {
138 		va_start(args, fmt);
139 		vcmn_err(CE_CONT, fmt, args);
140 		va_end(args);
141 	}
142 }
143 #endif
144 
145 void
146 zyd_warn(const int8_t *fmt, ...)
147 {
148 	va_list args;
149 
150 	va_start(args, fmt);
151 	vcmn_err(CE_WARN, fmt, args);
152 	va_end(args);
153 }
154 
155 /*
156  * Internal functions
157  */
158 static uint8_t
159 zyd_plcp_signal(uint16_t rate)
160 {
161 	switch (rate) {
162 		/* CCK rates (returned values are device-dependent) */
163 	case 2:
164 		return (0x0);
165 	case 4:
166 		return (0x1);
167 	case 11:
168 		return (0x2);
169 	case 22:
170 		return (0x3);
171 
172 		/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
173 	case 12:
174 		return (0xb);
175 	case 18:
176 		return (0xf);
177 	case 24:
178 		return (0xa);
179 	case 36:
180 		return (0xe);
181 	case 48:
182 		return (0x9);
183 	case 72:
184 		return (0xd);
185 	case 96:
186 		return (0x8);
187 	case 108:
188 		return (0xc);
189 
190 		/* unsupported rates (should not get there) */
191 	default:
192 		return (0xff);
193 	}
194 }
195 
196 /*
197  * Timeout function for scanning.
198  *
199  * Called at the end of each scanning round.
200  */
201 static void
202 zyd_next_scan(void *arg)
203 {
204 	struct zyd_softc *sc = arg;
205 	struct ieee80211com *ic = &sc->ic;
206 
207 	ZYD_DEBUG((ZYD_DBG_SCAN, "scan timer: fired\n"));
208 
209 	if (ic->ic_state == IEEE80211_S_SCAN) {
210 		ieee80211_next_scan(ic);
211 	} else {
212 		ZYD_DEBUG((ZYD_DBG_SCAN, "scan timer: no work\n"));
213 	}
214 }
215 
216 /*
217  * Extract a 802.11 frame from the received packet and forward it to net80211.
218  */
219 void
220 zyd_receive(struct zyd_softc *sc, const uint8_t *buf, uint16_t len)
221 {
222 	const struct zyd_rx_stat *stat;
223 	struct ieee80211com *ic = &sc->ic;
224 	struct ieee80211_frame *wh;
225 	struct ieee80211_node *in;
226 	int rlen;		/* Actual frame length */
227 	uint8_t rssi;
228 	mblk_t *m;
229 
230 	if (len < ZYD_MIN_FRAGSZ) {
231 		/* Packet is too short, silently drop it */
232 		sc->rx_err++;
233 		return;
234 	}
235 
236 	stat = (const struct zyd_rx_stat *)
237 	    (buf + len - sizeof (struct zyd_rx_stat));
238 	if (stat->flags & ZYD_RX_ERROR) {
239 		/* Frame is corrupted, silently drop it */
240 		sc->rx_err++;
241 		return;
242 	}
243 
244 	/* compute actual frame length */
245 	rlen = len - sizeof (struct zyd_plcphdr) -
246 	    sizeof (struct zyd_rx_stat) - IEEE80211_CRC_LEN;
247 
248 	m = allocb(rlen, BPRI_MED);
249 	if (m == NULL) {
250 		sc->rx_nobuf++;
251 		return;
252 	}
253 
254 	/* Copy frame to new buffer */
255 	bcopy(buf + sizeof (struct zyd_plcphdr), m->b_wptr, rlen);
256 	m->b_wptr += rlen;
257 
258 	/* Send frame to net80211 stack */
259 	wh = (struct ieee80211_frame *)m->b_rptr;
260 	in = ieee80211_find_rxnode(ic, wh);
261 	rssi = (stat->rssi < 25) ? 230 : (255 - stat->rssi) / 2;
262 
263 	(void) ieee80211_input(ic, m, in, (int32_t)rssi, 0);
264 
265 	ieee80211_free_node(in);
266 }
267 
268 /*
269  * xxx_send callback for net80211.
270  *
271  * Transmit a 802.11 frame.
272  *
273  * Constructs a packet from zyd_tx_header and 802.11 frame data
274  * and sends it to the chip.
275  */
276 /*ARGSUSED*/
277 static int
278 zyd_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
279 {
280 	struct zyd_softc *sc = ZYD_IC_TO_SOFTC(ic);
281 	struct zyd_tx_header *buf_hdr;
282 	struct ieee80211_frame *wh;
283 	struct ieee80211_node *in;
284 	struct ieee80211_key *k;
285 	mblk_t *m, *m0;
286 	int len, off, mblen;
287 	uint16_t frame_size, additional_size, rate;
288 	uint8_t service;
289 	int res;
290 
291 	ASSERT(mp->b_next == NULL);
292 
293 	/* device not ready, drop all frames */
294 	if (!sc->usb.connected || sc->suspended || !sc->running) {
295 		freemsg(mp);
296 		if (type == IEEE80211_FC0_TYPE_DATA)
297 			return (DDI_SUCCESS);
298 		else
299 			return (DDI_FAILURE);
300 	}
301 
302 	/* device queue overrun */
303 	if (sc->tx_queued >= ZYD_TX_LIST_COUNT) {
304 		/* drop management frames */
305 		if (type != IEEE80211_FC0_TYPE_DATA) {
306 			freemsg(mp);
307 		} else {
308 			(void) zyd_serial_enter(sc, ZYD_NO_SIG);
309 			sc->resched = B_TRUE;
310 			zyd_serial_exit(sc);
311 		}
312 		return (DDI_FAILURE);
313 	}
314 
315 	m = allocb(msgdsize(mp) + sizeof (struct zyd_tx_header) + 32,
316 	    BPRI_MED);
317 	if (m == NULL) {
318 		sc->tx_nobuf++;
319 		(void) zyd_serial_enter(sc, ZYD_NO_SIG);
320 		sc->resched = B_TRUE;
321 		zyd_serial_exit(sc);
322 		return (DDI_FAILURE);
323 	}
324 	m->b_rptr += sizeof (struct zyd_tx_header);
325 	m->b_wptr = m->b_rptr;
326 
327 	for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) {
328 		mblen = MBLKL(m0);
329 		(void) memcpy(m->b_rptr + off, m0->b_rptr, mblen);
330 		off += mblen;
331 	}
332 	m->b_wptr += off;
333 
334 	wh = (struct ieee80211_frame *)m->b_rptr;
335 	in = ieee80211_find_txnode(ic, wh->i_addr1);
336 
337 	if (in == NULL) {
338 		freemsg(m);
339 		sc->tx_err++;
340 		freemsg(mp);
341 		return (DDI_SUCCESS);
342 	}
343 	in->in_inact = 0;
344 
345 	if (type == IEEE80211_FC0_TYPE_DATA)
346 		(void) ieee80211_encap(ic, m, in);
347 
348 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
349 		k = ieee80211_crypto_encap(ic, m);
350 		if (k == NULL) {
351 			sc->tx_err++;
352 			ieee80211_free_node(in);
353 			freemsg(m);
354 			freemsg(mp);
355 			return (DDI_SUCCESS);
356 		}
357 		/* packet header may have moved, reset our local pointer */
358 		wh = (struct ieee80211_frame *)m->b_rptr;
359 	}
360 
361 	/*
362 	 * pickup a rate. May need work to make adaptive - at present,
363 	 * picks best rate for mode.
364 	 */
365 	if (type == IEEE80211_FC0_TYPE_MGT) {
366 		/* mgmt frames are sent at 1M */
367 		rate = (uint16_t)in->in_rates.ir_rates[0];
368 	} else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
369 		rate = (uint16_t)ic->ic_sup_rates[ic->ic_curmode].
370 		    ir_rates[ic->ic_fixed_rate];
371 	} else {
372 		rate = (uint16_t)ic->ic_sup_rates[ic->ic_curmode].
373 		    ir_rates[in->in_txrate];
374 	}
375 	rate &= IEEE80211_RATE_VAL;
376 	if (rate == 0)		/* should not happen */
377 		rate = 2;
378 
379 	/* Get total length of frame */
380 	len = msgsize(m);
381 
382 	m->b_rptr -= sizeof (struct zyd_tx_header);
383 	buf_hdr = (struct zyd_tx_header *)m->b_rptr;
384 
385 	frame_size = (uint16_t)len + 4;	/* include CRC32 */
386 	buf_hdr->frame_size = LE_16(frame_size);
387 
388 	/*
389 	 * Compute "packet size". What the 10 stands for,
390 	 * nobody knows.
391 	 */
392 	additional_size = sizeof (struct zyd_tx_header) + 10;
393 	if (sc->mac_rev == ZYD_ZD1211)
394 		buf_hdr->packet_size = LE_16(frame_size + additional_size);
395 	else
396 		buf_hdr->packet_size = LE_16(additional_size);
397 
398 	buf_hdr->rate_mod_flags = LE_8(zyd_plcp_signal(rate));
399 	buf_hdr->type_flags = LE_8(ZYD_TX_FLAG_BACKOFF);
400 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
401 		/* multicast frames are not sent at OFDM rates in 802.11b/g */
402 		if (frame_size > ic->ic_rtsthreshold) {
403 			buf_hdr->type_flags |= ZYD_TX_FLAG_RTS;
404 		} else if (ZYD_RATE_IS_OFDM(rate) &&
405 		    (ic->ic_flags & IEEE80211_F_USEPROT)) {
406 			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
407 				buf_hdr->type_flags |=
408 				    ZYD_TX_FLAG_CTS_TO_SELF;
409 			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
410 				buf_hdr->type_flags |= ZYD_TX_FLAG_RTS;
411 		}
412 	} else
413 		buf_hdr->type_flags |= ZYD_TX_FLAG_MULTICAST;
414 
415 	if ((type == IEEE80211_FC0_TYPE_CTL) &&
416 	    (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
417 	    == IEEE80211_FC0_SUBTYPE_PS_POLL)
418 		buf_hdr->type_flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);
419 
420 	if (ZYD_RATE_IS_OFDM(rate)) {
421 		buf_hdr->rate_mod_flags |= ZYD_TX_RMF_OFDM;
422 		if (ic->ic_curmode == IEEE80211_MODE_11A)
423 			buf_hdr->rate_mod_flags |= ZYD_TX_RMF_5GHZ;
424 	} else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
425 		buf_hdr->rate_mod_flags |= ZYD_TX_RMF_SH_PREAMBLE;
426 
427 	/*
428 	 * Compute frame duration and length-extension service flag.
429 	 */
430 	service = 0x00;
431 
432 	buf_hdr->frame_duration = LE_16((16 * frame_size + rate - 1) / rate);
433 	buf_hdr->service = service;
434 	buf_hdr->next_frame_duration = LE_16(0);
435 
436 	if (rate == 22) {
437 		const int remainder = (16 * frame_size) % 22;
438 		if (remainder != 0 && remainder < 7)
439 			buf_hdr->service |= ZYD_TX_SERVICE_LENGTH_EXTENSION;
440 	}
441 
442 	res = zyd_usb_send_packet(&sc->usb, m);
443 	if (res != ZYD_SUCCESS) {
444 		sc->tx_err++;
445 	} else {
446 		(void) zyd_serial_enter(sc, ZYD_NO_SIG);
447 		sc->tx_queued++;
448 		zyd_serial_exit(sc);
449 		freemsg(mp);
450 		ic->ic_stats.is_tx_frags++;
451 		ic->ic_stats.is_tx_bytes += len;
452 	}
453 
454 	ieee80211_free_node(in);
455 
456 	return (DDI_SUCCESS);
457 }
458 
459 /*
460  * Register with the MAC layer.
461  */
462 static zyd_res
463 zyd_mac_init(struct zyd_softc *sc)
464 {
465 	struct ieee80211com *ic = &sc->ic;
466 	mac_register_t *macp;
467 	wifi_data_t wd = { 0 };
468 	int err;
469 
470 	/*
471 	 * Initialize mac structure
472 	 */
473 	macp = mac_alloc(MAC_VERSION);
474 	if (macp == NULL) {
475 		ZYD_WARN("failed to allocate MAC structure\n");
476 		return (ZYD_FAILURE);
477 	}
478 
479 	/*
480 	 * Initialize pointer to device specific functions
481 	 */
482 	wd.wd_secalloc = WIFI_SEC_NONE;
483 	wd.wd_opmode = sc->ic.ic_opmode;
484 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_macaddr);
485 
486 	macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI;
487 	macp->m_driver = sc;
488 	macp->m_dip = sc->dip;
489 	macp->m_src_addr = ic->ic_macaddr;
490 	macp->m_callbacks = &zyd_m_callbacks;
491 	macp->m_min_sdu = 0;
492 	macp->m_max_sdu = IEEE80211_MTU;
493 	macp->m_pdata = &wd;
494 	macp->m_pdata_size = sizeof (wd);
495 
496 	/*
497 	 * Register the macp to mac
498 	 */
499 	err = mac_register(macp, &sc->ic.ic_mach);
500 	mac_free(macp);
501 
502 	if (err != DDI_SUCCESS) {
503 		ZYD_WARN("failed to register MAC structure\n");
504 		return (ZYD_FAILURE);
505 	}
506 
507 	return (ZYD_SUCCESS);
508 }
509 
510 /*
511  * Register with net80211.
512  */
513 static void
514 zyd_wifi_init(struct zyd_softc *sc)
515 {
516 	struct ieee80211com *ic = &sc->ic;
517 	int i;
518 
519 	/*
520 	 * Initialize the WiFi part, which will be used by generic layer
521 	 */
522 	ic->ic_phytype = IEEE80211_T_OFDM;
523 	ic->ic_opmode = IEEE80211_M_STA;
524 	ic->ic_state = IEEE80211_S_INIT;
525 	ic->ic_maxrssi = 255;
526 	ic->ic_xmit = zyd_send;
527 
528 	/* set device capabilities */
529 	ic->ic_caps = IEEE80211_C_TXPMGT |	/* tx power management */
530 	    IEEE80211_C_SHPREAMBLE |		/* short preamble supported */
531 	    IEEE80211_C_SHSLOT | IEEE80211_C_WPA;	/* Support WPA/WPA2 */
532 
533 	/* Copy MAC address */
534 	IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->macaddr);
535 
536 	/*
537 	 * set supported .11b and .11g rates
538 	 */
539 	ic->ic_sup_rates[IEEE80211_MODE_11B] = zyd_rateset_11b;
540 	ic->ic_sup_rates[IEEE80211_MODE_11G] = zyd_rateset_11g;
541 
542 	/*
543 	 * set supported .11b and .11g channels(1 through 14)
544 	 */
545 	for (i = 1; i <= 14; i++) {
546 		ic->ic_sup_channels[i].ich_freq =
547 		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
548 		ic->ic_sup_channels[i].ich_flags =
549 		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
550 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
551 	}
552 
553 	/*
554 	 * Init generic layer (it cannot fail)
555 	 */
556 	ieee80211_attach(ic);
557 
558 	/* register WPA door */
559 	ieee80211_register_door(ic, ddi_driver_name(sc->dip),
560 	    ddi_get_instance(sc->dip));
561 
562 	/* Must be after attach! */
563 	sc->newstate = ic->ic_newstate;
564 	ic->ic_newstate = zyd_newstate;
565 
566 	ieee80211_media_init(ic);
567 	ic->ic_def_txkey = 0;
568 }
569 
570 /*
571  * Device operations
572  */
573 /*
574  * Binding the driver to a device.
575  *
576  * Concurrency: Until zyd_attach() returns with success,
577  * the only other entry point that can be executed is getinfo().
578  * Thus no locking here yet.
579  */
580 static int
581 zyd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
582 {
583 	struct zyd_softc *sc;
584 	char strbuf[32];
585 	int instance;
586 	int err;
587 
588 	switch (cmd) {
589 	case DDI_ATTACH:
590 		break;
591 
592 	case DDI_RESUME:
593 		sc = ddi_get_soft_state(zyd_ssp, ddi_get_instance(dip));
594 		ASSERT(sc != NULL);
595 
596 		(void) zyd_resume(sc);
597 		return (DDI_SUCCESS);
598 
599 	default:
600 		return (DDI_FAILURE);
601 	}
602 
603 	instance = ddi_get_instance(dip);
604 	err = ddi_soft_state_zalloc(zyd_ssp, instance);
605 
606 	if (err != DDI_SUCCESS) {
607 		ZYD_WARN("failed to allocate soft state\n");
608 		return (DDI_FAILURE);
609 	}
610 
611 	sc = ddi_get_soft_state(zyd_ssp, instance);
612 	sc->dip = dip;
613 	sc->timeout_id = 0;
614 
615 	if (zyd_usb_init(sc) != ZYD_SUCCESS) {
616 		ddi_soft_state_free(zyd_ssp, instance);
617 		return (DDI_FAILURE);
618 	}
619 
620 	if (zyd_hw_init(sc) != ZYD_SUCCESS) {
621 		zyd_usb_deinit(sc);
622 		ddi_soft_state_free(zyd_ssp, instance);
623 		return (DDI_FAILURE);
624 	}
625 
626 	zyd_wifi_init(sc);
627 
628 	if (zyd_mac_init(sc) != DDI_SUCCESS) {
629 		ieee80211_detach(&sc->ic);
630 		zyd_usb_deinit(sc);
631 		ddi_soft_state_free(zyd_ssp, instance);
632 		return (DDI_FAILURE);
633 	}
634 
635 	/*
636 	 * Create minor node of type DDI_NT_NET_WIFI
637 	 */
638 	(void) snprintf(strbuf, sizeof (strbuf), ZYD_DRV_NAME"%d", instance);
639 	err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
640 	    instance + 1, DDI_NT_NET_WIFI, 0);
641 	if (err != DDI_SUCCESS)
642 		ZYD_WARN("failed to create minor node\n");
643 
644 	/* initialize locking */
645 	zyd_serial_init(sc);
646 
647 	return (DDI_SUCCESS);
648 }
649 
650 /*
651  * Detach the driver from a device.
652  *
653  * Concurrency: Will be called only after a successful attach
654  * (and not concurrently).
655  */
656 static int
657 zyd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
658 {
659 	struct zyd_softc *sc = NULL;
660 
661 	switch (cmd) {
662 	case DDI_DETACH:
663 		break;
664 
665 	case DDI_SUSPEND:
666 		sc = ddi_get_soft_state(zyd_ssp, ddi_get_instance(dip));
667 		ASSERT(sc != NULL);
668 
669 		return (zyd_suspend(sc));
670 
671 	default:
672 		return (DDI_FAILURE);
673 	}
674 
675 	sc = ddi_get_soft_state(zyd_ssp, ddi_get_instance(dip));
676 	ASSERT(sc != NULL);
677 
678 	if (mac_disable(sc->ic.ic_mach) != 0)
679 		return (DDI_FAILURE);
680 	/*
681 	 * Unregister from the MAC layer subsystem
682 	 */
683 	(void) mac_unregister(sc->ic.ic_mach);
684 
685 	/*
686 	 * Detach ieee80211
687 	 */
688 	ieee80211_detach(&sc->ic);
689 
690 	zyd_hw_deinit(sc);
691 	zyd_usb_deinit(sc);
692 
693 	/* At this point it should be safe to release & destroy the locks */
694 	zyd_serial_deinit(sc);
695 
696 	ddi_remove_minor_node(dip, NULL);
697 	ddi_soft_state_free(zyd_ssp, ddi_get_instance(dip));
698 	return (DDI_SUCCESS);
699 }
700 
701 /*
702  * Mac Call Back functions
703  */
704 
705 /*
706  * Read device statistic information.
707  */
708 static int
709 zyd_m_stat(void *arg, uint_t stat, uint64_t *val)
710 {
711 	struct zyd_softc *sc = (struct zyd_softc *)arg;
712 	ieee80211com_t *ic = &sc->ic;
713 	ieee80211_node_t *in;
714 
715 	switch (stat) {
716 	case MAC_STAT_IFSPEED:
717 		if (!sc->usb.connected || sc->suspended || !sc->running)
718 			return (ENOTSUP);
719 		in = ieee80211_ref_node(ic->ic_bss);
720 		*val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
721 		    IEEE80211_RATE(in->in_txrate) :
722 		    ic->ic_fixed_rate) / 2 * 1000000;
723 		ieee80211_free_node(in);
724 		break;
725 	case MAC_STAT_NOXMTBUF:
726 		*val = sc->tx_nobuf;
727 		break;
728 	case MAC_STAT_NORCVBUF:
729 		*val = sc->rx_nobuf;
730 		break;
731 	case MAC_STAT_IERRORS:
732 		*val = sc->rx_err;
733 		break;
734 	case MAC_STAT_RBYTES:
735 		*val = ic->ic_stats.is_rx_bytes;
736 		break;
737 	case MAC_STAT_IPACKETS:
738 		*val = ic->ic_stats.is_rx_frags;
739 		break;
740 	case MAC_STAT_OBYTES:
741 		*val = ic->ic_stats.is_tx_bytes;
742 		break;
743 	case MAC_STAT_OPACKETS:
744 		*val = ic->ic_stats.is_tx_frags;
745 		break;
746 	case MAC_STAT_OERRORS:
747 	case WIFI_STAT_TX_FAILED:
748 		*val = sc->tx_err;
749 		break;
750 	case WIFI_STAT_TX_RETRANS:
751 	case WIFI_STAT_FCS_ERRORS:
752 	case WIFI_STAT_WEP_ERRORS:
753 	case WIFI_STAT_TX_FRAGS:
754 	case WIFI_STAT_MCAST_TX:
755 	case WIFI_STAT_RTS_SUCCESS:
756 	case WIFI_STAT_RTS_FAILURE:
757 	case WIFI_STAT_ACK_FAILURE:
758 	case WIFI_STAT_RX_FRAGS:
759 	case WIFI_STAT_MCAST_RX:
760 	case WIFI_STAT_RX_DUPS:
761 		return (ieee80211_stat(ic, stat, val));
762 	default:
763 		return (ENOTSUP);
764 	}
765 	return (0);
766 }
767 
768 /*
769  * Start the device.
770  *
771  * Concurrency: Presumably fully concurrent, must lock.
772  */
773 static int
774 zyd_m_start(void *arg)
775 {
776 	struct zyd_softc *sc = (struct zyd_softc *)arg;
777 
778 	(void) zyd_serial_enter(sc, ZYD_NO_SIG);
779 	if ((!sc->usb.connected) || (zyd_hw_start(sc) != ZYD_SUCCESS)) {
780 		zyd_serial_exit(sc);
781 		return (DDI_FAILURE);
782 	}
783 	zyd_serial_exit(sc);
784 
785 	ieee80211_new_state(&sc->ic, IEEE80211_S_INIT, -1);
786 	sc->running = B_TRUE;
787 
788 	return (DDI_SUCCESS);
789 }
790 
791 /*
792  * Stop the device.
793  */
794 static void
795 zyd_m_stop(void *arg)
796 {
797 	struct zyd_softc *sc = (struct zyd_softc *)arg;
798 
799 	sc->running = B_FALSE;
800 	ieee80211_new_state(&sc->ic, IEEE80211_S_INIT, -1);
801 
802 	(void) zyd_serial_enter(sc, ZYD_NO_SIG);
803 	sc->resched = B_FALSE;
804 	zyd_hw_stop(sc);
805 	zyd_serial_exit(sc);
806 }
807 
808 /*
809  * Change the MAC address of the device.
810  */
811 /*ARGSUSED*/
812 static int
813 zyd_m_unicst(void *arg, const uint8_t *macaddr)
814 {
815 	return (DDI_FAILURE);
816 }
817 
818 /*
819  * Enable/disable multicast.
820  */
821 /*ARGSUSED*/
822 static int
823 zyd_m_multicst(void *arg, boolean_t add, const uint8_t *m)
824 {
825 	ZYD_DEBUG((ZYD_DBG_GLD, "multicast not implemented\n"));
826 	return (DDI_SUCCESS);
827 }
828 
829 /*
830  * Enable/disable promiscuous mode.
831  */
832 /*ARGSUSED*/
833 static int
834 zyd_m_promisc(void *arg, boolean_t on)
835 {
836 	ZYD_DEBUG((ZYD_DBG_GLD, "promiscuous not implemented\n"));
837 	return (DDI_SUCCESS);
838 }
839 
840 /*
841  * IOCTL request.
842  */
843 static void
844 zyd_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
845 {
846 	struct zyd_softc *sc = (struct zyd_softc *)arg;
847 	struct ieee80211com *ic = &sc->ic;
848 
849 	if (!sc->usb.connected || sc->suspended || !sc->running) {
850 		miocnak(wq, mp, 0, ENXIO);
851 		return;
852 	}
853 
854 	if (ieee80211_ioctl(ic, wq, mp) == ENETRESET) {
855 		if (sc->running && ic->ic_des_esslen) {
856 			zyd_m_stop(sc);
857 			if (zyd_m_start(sc) != DDI_SUCCESS)
858 				return;
859 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
860 		}
861 	}
862 }
863 
864 /*
865  * callback functions for /get/set properties
866  */
867 static int
868 zyd_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
869     uint_t wldp_length, const void *wldp_buf)
870 {
871 	struct zyd_softc *sc = (struct zyd_softc *)arg;
872 	struct ieee80211com *ic = &sc->ic;
873 	int err;
874 
875 	if (!sc->usb.connected || sc->suspended || !sc->running) {
876 		return (ENXIO);
877 	}
878 
879 	err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
880 	    wldp_buf);
881 	if (err == ENETRESET) {
882 		if (sc->running && ic->ic_des_esslen) {
883 			zyd_m_stop(sc);
884 			if (zyd_m_start(sc) != DDI_SUCCESS)
885 				return (DDI_FAILURE);
886 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
887 		}
888 		err = 0;
889 	}
890 
891 	return (err);
892 }
893 
894 static int
895 zyd_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
896     uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm)
897 {
898 	struct zyd_softc *sc = (struct zyd_softc *)arg;
899 	int err;
900 
901 	if (!sc->usb.connected || sc->suspended || !sc->running) {
902 		return (DDI_FAILURE);
903 	}
904 
905 	err = ieee80211_getprop(&sc->ic, pr_name, wldp_pr_num,
906 	    pr_flags, wldp_length, wldp_buf, perm);
907 
908 	return (err);
909 }
910 
911 /*
912  * Transmit a data frame.
913  */
914 static mblk_t *
915 zyd_m_tx(void *arg, mblk_t *mp)
916 {
917 	struct zyd_softc *sc = (struct zyd_softc *)arg;
918 	struct ieee80211com *ic = &sc->ic;
919 	mblk_t *next;
920 
921 	ASSERT(mp != NULL);
922 
923 	/* not associated, drop data frames */
924 	if (ic->ic_state != IEEE80211_S_RUN) {
925 		freemsg(mp);
926 		return (DDI_SUCCESS);
927 	}
928 
929 	while (mp != NULL) {
930 		next = mp->b_next;
931 		mp->b_next = NULL;
932 
933 		if (zyd_send(ic, mp, IEEE80211_FC0_TYPE_DATA) != DDI_SUCCESS) {
934 			mp->b_next = next;
935 			break;
936 		}
937 		mp = next;
938 	}
939 
940 	return (mp);
941 }
942 
943 /*
944  * xxx_newstate callback for net80211.
945  *
946  * Called by net80211 whenever the ieee80211 state changes.
947  */
948 static int
949 zyd_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
950 {
951 	struct zyd_softc *sc = ZYD_IC_TO_SOFTC(ic);
952 	struct ieee80211_node *in;
953 	uint_t chan;
954 
955 	if (sc->timeout_id != 0) {
956 		(void) untimeout(sc->timeout_id);
957 		sc->timeout_id = 0;
958 	}
959 
960 	if (!sc->usb.connected || sc->suspended || !sc->running) {
961 		return (sc->newstate(ic, nstate, arg));
962 	}
963 
964 	switch (nstate) {
965 	case IEEE80211_S_SCAN:
966 		ZYD_DEBUG((ZYD_DBG_SCAN, "scan timer: starting next\n"));
967 		sc->timeout_id = timeout(zyd_next_scan, sc,
968 		    drv_usectohz(ZYD_DWELL_TIME));
969 		/*FALLTHRU*/
970 	case IEEE80211_S_AUTH:
971 	case IEEE80211_S_ASSOC:
972 	case IEEE80211_S_RUN:
973 		chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
974 		if (chan == 0 || chan == IEEE80211_CHAN_ANY) {
975 			ZYD_WARN("invalid channel number\n");
976 			return (0);
977 		}
978 		(void) zyd_serial_enter(sc, ZYD_SER_SIG);
979 		zyd_hw_set_channel(sc, chan);
980 		zyd_serial_exit(sc);
981 
982 		in = ic->ic_bss;
983 		in->in_txrate = in->in_rates.ir_nrates - 1;
984 	default:
985 		break;
986 	}
987 
988 	return (sc->newstate(ic, nstate, arg));
989 }
990 
991 /*
992  * USB-safe synchronization.
993  * Debugging routines.
994  *
995  * Kmutexes should never be held when making calls to USBA
996  * or when sleeping. Thus, we implement our own "mutex" on top
997  * of kmutexes and kcondvars.
998  *
999  * Usage: Any (possibly concurrent) access to the soft state or device must
1000  * be serialized with a pair of zyd_serial_enter()/zyd_serial_exit().
1001  */
1002 /*
1003  * Initialize the serialization object.
1004  */
1005 void
1006 zyd_serial_init(struct zyd_softc *sc)
1007 {
1008 	mutex_init(&sc->serial.lock, NULL, MUTEX_DRIVER,
1009 	    sc->usb.cdata->dev_iblock_cookie);
1010 	cv_init(&sc->serial.wait, NULL, CV_DRIVER, NULL);
1011 
1012 	sc->serial.held = B_FALSE;
1013 	sc->serial.initialized = B_TRUE;
1014 }
1015 
1016 /*
1017  * Wait for the serialization object.
1018  *
1019  * If wait_sig is ZYD_SER_SIG, the function may return
1020  * a signal is received. In this case, the serialization object
1021  * is not acquired (but the mutex is) and the return value is ZYD_FAILURE.
1022  *
1023  * In any other case the function returns ZYD_SUCCESS and the
1024  * serialization object is acquired.
1025  */
1026 zyd_res
1027 zyd_serial_enter(struct zyd_softc *sc, boolean_t wait_sig)
1028 {
1029 	zyd_res res;
1030 
1031 	mutex_enter(&sc->serial.lock);
1032 
1033 	res = ZYD_SUCCESS;
1034 
1035 	while (sc->serial.held != B_FALSE) {
1036 		if (wait_sig == ZYD_SER_SIG) {
1037 			res = cv_wait_sig(&sc->serial.wait, &sc->serial.lock);
1038 		} else {
1039 			cv_wait(&sc->serial.wait, &sc->serial.lock);
1040 		}
1041 	}
1042 	sc->serial.held = B_TRUE;
1043 
1044 	mutex_exit(&sc->serial.lock);
1045 
1046 	return (res);
1047 }
1048 
1049 /*
1050  * Release the serialization object.
1051  */
1052 void
1053 zyd_serial_exit(struct zyd_softc *sc)
1054 {
1055 	mutex_enter(&sc->serial.lock);
1056 	sc->serial.held = B_FALSE;
1057 	cv_broadcast(&sc->serial.wait);
1058 	mutex_exit(&sc->serial.lock);
1059 }
1060 
1061 /*
1062  * Destroy the serialization object.
1063  */
1064 void
1065 zyd_serial_deinit(struct zyd_softc *sc)
1066 {
1067 	cv_destroy(&sc->serial.wait);
1068 	mutex_destroy(&sc->serial.lock);
1069 
1070 	sc->serial.initialized = B_FALSE;
1071 }
1072 
1073 
1074 /*
1075  * zyd_cb_lock: a special signal structure that is used for notification
1076  * that a callback function has been called.
1077  */
1078 
1079 /* Initializes the zyd_cb_lock structure. */
1080 void
1081 zyd_cb_lock_init(struct zyd_cb_lock *lock)
1082 {
1083 	ASSERT(lock != NULL);
1084 	mutex_init(&lock->mutex, NULL, MUTEX_DRIVER, NULL);
1085 	cv_init(&lock->cv, NULL, CV_DRIVER, NULL);
1086 	lock->done = B_FALSE;
1087 }
1088 
1089 /* Deinitalizes the zyd_cb_lock structure. */
1090 void
1091 zyd_cb_lock_destroy(struct zyd_cb_lock *lock)
1092 {
1093 	ASSERT(lock != NULL);
1094 	mutex_destroy(&lock->mutex);
1095 	cv_destroy(&lock->cv);
1096 }
1097 
1098 /*
1099  * Wait on lock until someone calls the "signal" function or the timeout
1100  * expires. Note: timeout is in microseconds.
1101  */
1102 zyd_res
1103 zyd_cb_lock_wait(struct zyd_cb_lock *lock, clock_t timeout)
1104 {
1105 	zyd_res res;
1106 	clock_t etime;
1107 	int cv_res;
1108 
1109 	ASSERT(lock != NULL);
1110 
1111 	mutex_enter(&lock->mutex);
1112 
1113 	if (timeout < 0) {
1114 		/* no timeout - wait as long as needed */
1115 		while (lock->done == B_FALSE)
1116 			(void) cv_wait(&lock->cv, &lock->mutex);
1117 	} else {
1118 		/* wait with timeout (given in usec) */
1119 		etime = ddi_get_lbolt() + drv_usectohz(timeout);
1120 		while (lock->done == B_FALSE) {
1121 			cv_res =
1122 			    cv_timedwait_sig(&lock->cv, &lock->mutex, etime);
1123 			if (cv_res <= 0)
1124 				break;
1125 		}
1126 	}
1127 
1128 	res = (lock->done == B_TRUE) ? ZYD_SUCCESS : ZYD_FAILURE;
1129 
1130 	mutex_exit(&lock->mutex);
1131 
1132 	return (res);
1133 }
1134 
1135 /* Signal that the job (eg. callback) is done and unblock anyone who waits. */
1136 void
1137 zyd_cb_lock_signal(struct zyd_cb_lock *lock)
1138 {
1139 	ASSERT(lock != NULL);
1140 
1141 	mutex_enter(&lock->mutex);
1142 
1143 	lock->done = B_TRUE;
1144 	cv_broadcast(&lock->cv);
1145 
1146 	mutex_exit(&lock->mutex);
1147 }
1148 
1149 /*
1150  * Loadable module configuration entry points
1151  */
1152 
1153 /*
1154  * _init module entry point.
1155  *
1156  * Called when the module is being loaded into memory.
1157  */
1158 int
1159 _init(void)
1160 {
1161 	int err;
1162 
1163 	err = ddi_soft_state_init(&zyd_ssp, sizeof (struct zyd_softc), 1);
1164 
1165 	if (err != DDI_SUCCESS)
1166 		return (err);
1167 
1168 	mac_init_ops(&zyd_devops, ZYD_DRV_NAME);
1169 	err = mod_install(&zyd_ml);
1170 
1171 	if (err != DDI_SUCCESS) {
1172 		mac_fini_ops(&zyd_devops);
1173 		ddi_soft_state_fini(&zyd_ssp);
1174 	}
1175 
1176 	return (err);
1177 }
1178 
1179 /*
1180  * _info module entry point.
1181  *
1182  * Called to obtain information about the module.
1183  */
1184 int
1185 _info(struct modinfo *modinfop)
1186 {
1187 	return (mod_info(&zyd_ml, modinfop));
1188 }
1189 
1190 /*
1191  * _fini module entry point.
1192  *
1193  * Called when the module is being unloaded.
1194  */
1195 int
1196 _fini(void)
1197 {
1198 	int err;
1199 
1200 	err = mod_remove(&zyd_ml);
1201 	if (err == DDI_SUCCESS) {
1202 		mac_fini_ops(&zyd_devops);
1203 		ddi_soft_state_fini(&zyd_ssp);
1204 	}
1205 
1206 	return (err);
1207 }
1208