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