xref: /onnv-gate/usr/src/uts/common/io/zyd/zyd.c (revision 11878:ac93462db6d7)
18503SPengcheng.Chen@Sun.COM /*
2*11878SVenu.Iyer@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
38503SPengcheng.Chen@Sun.COM  * Use is subject to license terms.
48503SPengcheng.Chen@Sun.COM  */
58503SPengcheng.Chen@Sun.COM 
68503SPengcheng.Chen@Sun.COM /*
78503SPengcheng.Chen@Sun.COM  * Copyright (c) 2007 by  Lukas Turek <turek@ksvi.mff.cuni.cz>
88503SPengcheng.Chen@Sun.COM  * Copyright (c) 2007 by  Jiri Svoboda <jirik.svoboda@seznam.cz>
98503SPengcheng.Chen@Sun.COM  * Copyright (c) 2007 by  Martin Krulis <martin.krulis@matfyz.cz>
108503SPengcheng.Chen@Sun.COM  * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr>
118503SPengcheng.Chen@Sun.COM  * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de>
128503SPengcheng.Chen@Sun.COM  *
138503SPengcheng.Chen@Sun.COM  * Permission to use, copy, modify, and distribute this software for any
148503SPengcheng.Chen@Sun.COM  * purpose with or without fee is hereby granted, provided that the above
158503SPengcheng.Chen@Sun.COM  * copyright notice and this permission notice appear in all copies.
168503SPengcheng.Chen@Sun.COM  *
178503SPengcheng.Chen@Sun.COM  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
188503SPengcheng.Chen@Sun.COM  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
198503SPengcheng.Chen@Sun.COM  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
208503SPengcheng.Chen@Sun.COM  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
218503SPengcheng.Chen@Sun.COM  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
228503SPengcheng.Chen@Sun.COM  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
238503SPengcheng.Chen@Sun.COM  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
248503SPengcheng.Chen@Sun.COM  *
258503SPengcheng.Chen@Sun.COM  */
268503SPengcheng.Chen@Sun.COM 
278503SPengcheng.Chen@Sun.COM /*
288503SPengcheng.Chen@Sun.COM  * ZD1211 wLAN driver
298503SPengcheng.Chen@Sun.COM  * Driver major routines
308503SPengcheng.Chen@Sun.COM  */
318503SPengcheng.Chen@Sun.COM 
328503SPengcheng.Chen@Sun.COM #include <sys/byteorder.h>
338503SPengcheng.Chen@Sun.COM #include <sys/ddi.h>
348503SPengcheng.Chen@Sun.COM #include <sys/sunddi.h>
358503SPengcheng.Chen@Sun.COM #include <sys/conf.h>
368503SPengcheng.Chen@Sun.COM #include <sys/modctl.h>
378503SPengcheng.Chen@Sun.COM #include <sys/mac_provider.h>
388503SPengcheng.Chen@Sun.COM #include <sys/mac_wifi.h>
398503SPengcheng.Chen@Sun.COM #include <sys/strsun.h>
408503SPengcheng.Chen@Sun.COM #include <sys/ksynch.h>
418503SPengcheng.Chen@Sun.COM 
428503SPengcheng.Chen@Sun.COM #include "zyd.h"
438503SPengcheng.Chen@Sun.COM #include "zyd_reg.h"
448503SPengcheng.Chen@Sun.COM 
458503SPengcheng.Chen@Sun.COM static int zyd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
468503SPengcheng.Chen@Sun.COM static int zyd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
478503SPengcheng.Chen@Sun.COM 
488503SPengcheng.Chen@Sun.COM static int zyd_m_stat(void *arg, uint_t stat, uint64_t *val);
498503SPengcheng.Chen@Sun.COM static int zyd_m_start(void *arg);
508503SPengcheng.Chen@Sun.COM static void zyd_m_stop(void *arg);
518503SPengcheng.Chen@Sun.COM static int zyd_m_unicst(void *arg, const uint8_t *macaddr);
528503SPengcheng.Chen@Sun.COM static int zyd_m_multicst(void *arg, boolean_t add, const uint8_t *m);
538503SPengcheng.Chen@Sun.COM static int zyd_m_promisc(void *arg, boolean_t on);
548503SPengcheng.Chen@Sun.COM static void zyd_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
558503SPengcheng.Chen@Sun.COM static mblk_t *zyd_m_tx(void *arg, mblk_t *mp);
568503SPengcheng.Chen@Sun.COM static int zyd_m_getprop(void *arg, const char *pr_name,
57*11878SVenu.Iyer@Sun.COM     mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf);
58*11878SVenu.Iyer@Sun.COM static void zyd_m_propinfo(void *arg, const char *pr_name,
59*11878SVenu.Iyer@Sun.COM     mac_prop_id_t wldp_pr_num, mac_prop_info_handle_t mph);
608503SPengcheng.Chen@Sun.COM static int zyd_m_setprop(void *arg, const char *pr_name,
618503SPengcheng.Chen@Sun.COM     mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf);
628503SPengcheng.Chen@Sun.COM 
638503SPengcheng.Chen@Sun.COM static int zyd_newstate(struct ieee80211com *ic,
648503SPengcheng.Chen@Sun.COM     enum ieee80211_state state, int arg);
658503SPengcheng.Chen@Sun.COM 
668503SPengcheng.Chen@Sun.COM /* Driver identification */
678503SPengcheng.Chen@Sun.COM static char zyd_ident[] = ZYD_DRV_DESC " " ZYD_DRV_REV;
688503SPengcheng.Chen@Sun.COM 
698503SPengcheng.Chen@Sun.COM /* Global state pointer for managing per-device soft states */
708503SPengcheng.Chen@Sun.COM void *zyd_ssp;
718503SPengcheng.Chen@Sun.COM 
728503SPengcheng.Chen@Sun.COM /*
738503SPengcheng.Chen@Sun.COM  * Mac Call Back entries
748503SPengcheng.Chen@Sun.COM  */
758503SPengcheng.Chen@Sun.COM static mac_callbacks_t zyd_m_callbacks = {
76*11878SVenu.Iyer@Sun.COM 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
778503SPengcheng.Chen@Sun.COM 	zyd_m_stat,		/* Get the value of a statistic */
788503SPengcheng.Chen@Sun.COM 	zyd_m_start,		/* Start the device */
798503SPengcheng.Chen@Sun.COM 	zyd_m_stop,		/* Stop the device */
808503SPengcheng.Chen@Sun.COM 	zyd_m_promisc,		/* Enable or disable promiscuous mode */
818503SPengcheng.Chen@Sun.COM 	zyd_m_multicst,		/* Enable or disable a multicast addr */
828503SPengcheng.Chen@Sun.COM 	zyd_m_unicst,		/* Set the unicast MAC address */
838503SPengcheng.Chen@Sun.COM 	zyd_m_tx,		/* Transmit a packet */
84*11878SVenu.Iyer@Sun.COM 	NULL,
858503SPengcheng.Chen@Sun.COM 	zyd_m_ioctl,		/* Process an unknown ioctl */
868503SPengcheng.Chen@Sun.COM 	NULL,			/* mc_getcapab */
878503SPengcheng.Chen@Sun.COM 	NULL,
888503SPengcheng.Chen@Sun.COM 	NULL,
898503SPengcheng.Chen@Sun.COM 	zyd_m_setprop,
90*11878SVenu.Iyer@Sun.COM 	zyd_m_getprop,
91*11878SVenu.Iyer@Sun.COM 	zyd_m_propinfo
928503SPengcheng.Chen@Sun.COM };
938503SPengcheng.Chen@Sun.COM 
948503SPengcheng.Chen@Sun.COM /*
958503SPengcheng.Chen@Sun.COM  *  Module Loading Data & Entry Points
968503SPengcheng.Chen@Sun.COM  */
978503SPengcheng.Chen@Sun.COM DDI_DEFINE_STREAM_OPS(zyd_devops,	/* name */
988503SPengcheng.Chen@Sun.COM     nulldev,			/* identify */
998503SPengcheng.Chen@Sun.COM     nulldev,			/* probe */
1008503SPengcheng.Chen@Sun.COM     zyd_attach,			/* attach */
1018503SPengcheng.Chen@Sun.COM     zyd_detach,			/* detach */
1028503SPengcheng.Chen@Sun.COM     nodev,			/* reset */
1038503SPengcheng.Chen@Sun.COM     NULL,			/* getinfo */
1048503SPengcheng.Chen@Sun.COM     D_MP,			/* flag */
1058503SPengcheng.Chen@Sun.COM     NULL,			/* stream_tab */
1068503SPengcheng.Chen@Sun.COM     ddi_quiesce_not_needed	/* quiesce */
1078503SPengcheng.Chen@Sun.COM );
1088503SPengcheng.Chen@Sun.COM 
1098503SPengcheng.Chen@Sun.COM static struct modldrv zyd_modldrv = {
1108503SPengcheng.Chen@Sun.COM 	&mod_driverops,		/* drv_modops */
1118503SPengcheng.Chen@Sun.COM 	zyd_ident,		/* drv_linkinfo */
1128503SPengcheng.Chen@Sun.COM 	&zyd_devops		/* drv_dev_ops */
1138503SPengcheng.Chen@Sun.COM };
1148503SPengcheng.Chen@Sun.COM 
1158503SPengcheng.Chen@Sun.COM static struct modlinkage zyd_ml = {
1168503SPengcheng.Chen@Sun.COM 	MODREV_1,		/* ml_rev */
1178503SPengcheng.Chen@Sun.COM 	{&zyd_modldrv, NULL}	/* ml_linkage */
1188503SPengcheng.Chen@Sun.COM };
1198503SPengcheng.Chen@Sun.COM 
1208503SPengcheng.Chen@Sun.COM /*
1218503SPengcheng.Chen@Sun.COM  * Wireless-specific structures
1228503SPengcheng.Chen@Sun.COM  */
1238503SPengcheng.Chen@Sun.COM static const struct ieee80211_rateset zyd_rateset_11b = {
1248503SPengcheng.Chen@Sun.COM 	4, {2, 4, 11, 22}	/* units are 0.5Mbit! */
1258503SPengcheng.Chen@Sun.COM };
1268503SPengcheng.Chen@Sun.COM 
1278503SPengcheng.Chen@Sun.COM static const struct ieee80211_rateset zyd_rateset_11g = {
1288503SPengcheng.Chen@Sun.COM 	12, {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108}
1298503SPengcheng.Chen@Sun.COM };
1308503SPengcheng.Chen@Sun.COM 
1318503SPengcheng.Chen@Sun.COM 
1328503SPengcheng.Chen@Sun.COM #ifdef DEBUG
1338503SPengcheng.Chen@Sun.COM uint32_t zyd_dbg_flags;
1348503SPengcheng.Chen@Sun.COM 
1358503SPengcheng.Chen@Sun.COM void
zyd_dbg(uint32_t dbg_mask,const int8_t * fmt,...)1368503SPengcheng.Chen@Sun.COM zyd_dbg(uint32_t dbg_mask, const int8_t *fmt, ...)
1378503SPengcheng.Chen@Sun.COM {
1388503SPengcheng.Chen@Sun.COM 	va_list args;
1398503SPengcheng.Chen@Sun.COM 
1408503SPengcheng.Chen@Sun.COM 	if (dbg_mask & zyd_dbg_flags) {
1418503SPengcheng.Chen@Sun.COM 		va_start(args, fmt);
1428503SPengcheng.Chen@Sun.COM 		vcmn_err(CE_CONT, fmt, args);
1438503SPengcheng.Chen@Sun.COM 		va_end(args);
1448503SPengcheng.Chen@Sun.COM 	}
1458503SPengcheng.Chen@Sun.COM }
1468503SPengcheng.Chen@Sun.COM #endif
1478503SPengcheng.Chen@Sun.COM 
1488503SPengcheng.Chen@Sun.COM void
zyd_warn(const int8_t * fmt,...)1498503SPengcheng.Chen@Sun.COM zyd_warn(const int8_t *fmt, ...)
1508503SPengcheng.Chen@Sun.COM {
1518503SPengcheng.Chen@Sun.COM 	va_list args;
1528503SPengcheng.Chen@Sun.COM 
1538503SPengcheng.Chen@Sun.COM 	va_start(args, fmt);
1548503SPengcheng.Chen@Sun.COM 	vcmn_err(CE_WARN, fmt, args);
1558503SPengcheng.Chen@Sun.COM 	va_end(args);
1568503SPengcheng.Chen@Sun.COM }
1578503SPengcheng.Chen@Sun.COM 
1588503SPengcheng.Chen@Sun.COM /*
1598503SPengcheng.Chen@Sun.COM  * Internal functions
1608503SPengcheng.Chen@Sun.COM  */
1618503SPengcheng.Chen@Sun.COM static uint8_t
zyd_plcp_signal(uint16_t rate)1628503SPengcheng.Chen@Sun.COM zyd_plcp_signal(uint16_t rate)
1638503SPengcheng.Chen@Sun.COM {
1648503SPengcheng.Chen@Sun.COM 	switch (rate) {
1658503SPengcheng.Chen@Sun.COM 		/* CCK rates (returned values are device-dependent) */
1668503SPengcheng.Chen@Sun.COM 	case 2:
1678503SPengcheng.Chen@Sun.COM 		return (0x0);
1688503SPengcheng.Chen@Sun.COM 	case 4:
1698503SPengcheng.Chen@Sun.COM 		return (0x1);
1708503SPengcheng.Chen@Sun.COM 	case 11:
1718503SPengcheng.Chen@Sun.COM 		return (0x2);
1728503SPengcheng.Chen@Sun.COM 	case 22:
1738503SPengcheng.Chen@Sun.COM 		return (0x3);
1748503SPengcheng.Chen@Sun.COM 
1758503SPengcheng.Chen@Sun.COM 		/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
1768503SPengcheng.Chen@Sun.COM 	case 12:
1778503SPengcheng.Chen@Sun.COM 		return (0xb);
1788503SPengcheng.Chen@Sun.COM 	case 18:
1798503SPengcheng.Chen@Sun.COM 		return (0xf);
1808503SPengcheng.Chen@Sun.COM 	case 24:
1818503SPengcheng.Chen@Sun.COM 		return (0xa);
1828503SPengcheng.Chen@Sun.COM 	case 36:
1838503SPengcheng.Chen@Sun.COM 		return (0xe);
1848503SPengcheng.Chen@Sun.COM 	case 48:
1858503SPengcheng.Chen@Sun.COM 		return (0x9);
1868503SPengcheng.Chen@Sun.COM 	case 72:
1878503SPengcheng.Chen@Sun.COM 		return (0xd);
1888503SPengcheng.Chen@Sun.COM 	case 96:
1898503SPengcheng.Chen@Sun.COM 		return (0x8);
1908503SPengcheng.Chen@Sun.COM 	case 108:
1918503SPengcheng.Chen@Sun.COM 		return (0xc);
1928503SPengcheng.Chen@Sun.COM 
1938503SPengcheng.Chen@Sun.COM 		/* unsupported rates (should not get there) */
1948503SPengcheng.Chen@Sun.COM 	default:
1958503SPengcheng.Chen@Sun.COM 		return (0xff);
1968503SPengcheng.Chen@Sun.COM 	}
1978503SPengcheng.Chen@Sun.COM }
1988503SPengcheng.Chen@Sun.COM 
1998503SPengcheng.Chen@Sun.COM /*
2008503SPengcheng.Chen@Sun.COM  * Timeout function for scanning.
2018503SPengcheng.Chen@Sun.COM  *
2028503SPengcheng.Chen@Sun.COM  * Called at the end of each scanning round.
2038503SPengcheng.Chen@Sun.COM  */
2048503SPengcheng.Chen@Sun.COM static void
zyd_next_scan(void * arg)2058503SPengcheng.Chen@Sun.COM zyd_next_scan(void *arg)
2068503SPengcheng.Chen@Sun.COM {
2078503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = arg;
2088503SPengcheng.Chen@Sun.COM 	struct ieee80211com *ic = &sc->ic;
2098503SPengcheng.Chen@Sun.COM 
2108503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_SCAN, "scan timer: fired\n"));
2118503SPengcheng.Chen@Sun.COM 
2128503SPengcheng.Chen@Sun.COM 	if (ic->ic_state == IEEE80211_S_SCAN) {
2138503SPengcheng.Chen@Sun.COM 		ieee80211_next_scan(ic);
2148503SPengcheng.Chen@Sun.COM 	} else {
2158503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_SCAN, "scan timer: no work\n"));
2168503SPengcheng.Chen@Sun.COM 	}
2178503SPengcheng.Chen@Sun.COM }
2188503SPengcheng.Chen@Sun.COM 
2198503SPengcheng.Chen@Sun.COM /*
2208503SPengcheng.Chen@Sun.COM  * Extract a 802.11 frame from the received packet and forward it to net80211.
2218503SPengcheng.Chen@Sun.COM  */
2228503SPengcheng.Chen@Sun.COM void
zyd_receive(struct zyd_softc * sc,const uint8_t * buf,uint16_t len)2238503SPengcheng.Chen@Sun.COM zyd_receive(struct zyd_softc *sc, const uint8_t *buf, uint16_t len)
2248503SPengcheng.Chen@Sun.COM {
2258503SPengcheng.Chen@Sun.COM 	const struct zyd_rx_stat *stat;
2268503SPengcheng.Chen@Sun.COM 	struct ieee80211com *ic = &sc->ic;
2278503SPengcheng.Chen@Sun.COM 	struct ieee80211_frame *wh;
2288503SPengcheng.Chen@Sun.COM 	struct ieee80211_node *in;
2298503SPengcheng.Chen@Sun.COM 	int rlen;		/* Actual frame length */
2308503SPengcheng.Chen@Sun.COM 	uint8_t rssi;
2318503SPengcheng.Chen@Sun.COM 	mblk_t *m;
2328503SPengcheng.Chen@Sun.COM 
2338503SPengcheng.Chen@Sun.COM 	if (len < ZYD_MIN_FRAGSZ) {
2348503SPengcheng.Chen@Sun.COM 		/* Packet is too short, silently drop it */
2358503SPengcheng.Chen@Sun.COM 		sc->rx_err++;
2368503SPengcheng.Chen@Sun.COM 		return;
2378503SPengcheng.Chen@Sun.COM 	}
2388503SPengcheng.Chen@Sun.COM 
2398503SPengcheng.Chen@Sun.COM 	stat = (const struct zyd_rx_stat *)
2408503SPengcheng.Chen@Sun.COM 	    (buf + len - sizeof (struct zyd_rx_stat));
2418503SPengcheng.Chen@Sun.COM 	if (stat->flags & ZYD_RX_ERROR) {
2428503SPengcheng.Chen@Sun.COM 		/* Frame is corrupted, silently drop it */
2438503SPengcheng.Chen@Sun.COM 		sc->rx_err++;
2448503SPengcheng.Chen@Sun.COM 		return;
2458503SPengcheng.Chen@Sun.COM 	}
2468503SPengcheng.Chen@Sun.COM 
2478503SPengcheng.Chen@Sun.COM 	/* compute actual frame length */
2488503SPengcheng.Chen@Sun.COM 	rlen = len - sizeof (struct zyd_plcphdr) -
2498503SPengcheng.Chen@Sun.COM 	    sizeof (struct zyd_rx_stat) - IEEE80211_CRC_LEN;
2508503SPengcheng.Chen@Sun.COM 
2518503SPengcheng.Chen@Sun.COM 	m = allocb(rlen, BPRI_MED);
2528503SPengcheng.Chen@Sun.COM 	if (m == NULL) {
2538503SPengcheng.Chen@Sun.COM 		sc->rx_nobuf++;
2548503SPengcheng.Chen@Sun.COM 		return;
2558503SPengcheng.Chen@Sun.COM 	}
2568503SPengcheng.Chen@Sun.COM 
2578503SPengcheng.Chen@Sun.COM 	/* Copy frame to new buffer */
2588503SPengcheng.Chen@Sun.COM 	bcopy(buf + sizeof (struct zyd_plcphdr), m->b_wptr, rlen);
2598503SPengcheng.Chen@Sun.COM 	m->b_wptr += rlen;
2608503SPengcheng.Chen@Sun.COM 
2618503SPengcheng.Chen@Sun.COM 	/* Send frame to net80211 stack */
2628503SPengcheng.Chen@Sun.COM 	wh = (struct ieee80211_frame *)m->b_rptr;
2638503SPengcheng.Chen@Sun.COM 	in = ieee80211_find_rxnode(ic, wh);
2648503SPengcheng.Chen@Sun.COM 	rssi = (stat->rssi < 25) ? 230 : (255 - stat->rssi) / 2;
2658503SPengcheng.Chen@Sun.COM 
2668503SPengcheng.Chen@Sun.COM 	(void) ieee80211_input(ic, m, in, (int32_t)rssi, 0);
2678503SPengcheng.Chen@Sun.COM 
2688503SPengcheng.Chen@Sun.COM 	ieee80211_free_node(in);
2698503SPengcheng.Chen@Sun.COM }
2708503SPengcheng.Chen@Sun.COM 
2718503SPengcheng.Chen@Sun.COM /*
2728503SPengcheng.Chen@Sun.COM  * xxx_send callback for net80211.
2738503SPengcheng.Chen@Sun.COM  *
2748503SPengcheng.Chen@Sun.COM  * Transmit a 802.11 frame.
2758503SPengcheng.Chen@Sun.COM  *
2768503SPengcheng.Chen@Sun.COM  * Constructs a packet from zyd_tx_header and 802.11 frame data
2778503SPengcheng.Chen@Sun.COM  * and sends it to the chip.
2788503SPengcheng.Chen@Sun.COM  */
2798503SPengcheng.Chen@Sun.COM /*ARGSUSED*/
2808503SPengcheng.Chen@Sun.COM static int
zyd_send(ieee80211com_t * ic,mblk_t * mp,uint8_t type)2818503SPengcheng.Chen@Sun.COM zyd_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
2828503SPengcheng.Chen@Sun.COM {
2838503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = ZYD_IC_TO_SOFTC(ic);
2848503SPengcheng.Chen@Sun.COM 	struct zyd_tx_header *buf_hdr;
2858503SPengcheng.Chen@Sun.COM 	struct ieee80211_frame *wh;
2868503SPengcheng.Chen@Sun.COM 	struct ieee80211_node *in;
2878503SPengcheng.Chen@Sun.COM 	struct ieee80211_key *k;
2888503SPengcheng.Chen@Sun.COM 	mblk_t *m, *m0;
2898503SPengcheng.Chen@Sun.COM 	int len, off, mblen;
2908503SPengcheng.Chen@Sun.COM 	uint16_t frame_size, additional_size, rate;
2918503SPengcheng.Chen@Sun.COM 	uint8_t service;
2928503SPengcheng.Chen@Sun.COM 	int res;
2938503SPengcheng.Chen@Sun.COM 
2948503SPengcheng.Chen@Sun.COM 	ASSERT(mp->b_next == NULL);
2958503SPengcheng.Chen@Sun.COM 
2968503SPengcheng.Chen@Sun.COM 	/* device not ready, drop all frames */
2978503SPengcheng.Chen@Sun.COM 	if (!sc->usb.connected || sc->suspended || !sc->running) {
2988503SPengcheng.Chen@Sun.COM 		freemsg(mp);
2998503SPengcheng.Chen@Sun.COM 		if (type == IEEE80211_FC0_TYPE_DATA)
3008503SPengcheng.Chen@Sun.COM 			return (DDI_SUCCESS);
3018503SPengcheng.Chen@Sun.COM 		else
3028503SPengcheng.Chen@Sun.COM 			return (DDI_FAILURE);
3038503SPengcheng.Chen@Sun.COM 	}
3048503SPengcheng.Chen@Sun.COM 
3058503SPengcheng.Chen@Sun.COM 	/* device queue overrun */
3068503SPengcheng.Chen@Sun.COM 	if (sc->tx_queued >= ZYD_TX_LIST_COUNT) {
3078503SPengcheng.Chen@Sun.COM 		/* drop management frames */
3088503SPengcheng.Chen@Sun.COM 		if (type != IEEE80211_FC0_TYPE_DATA) {
3098503SPengcheng.Chen@Sun.COM 			freemsg(mp);
3108503SPengcheng.Chen@Sun.COM 		} else {
3118503SPengcheng.Chen@Sun.COM 			(void) zyd_serial_enter(sc, ZYD_NO_SIG);
3128503SPengcheng.Chen@Sun.COM 			sc->resched = B_TRUE;
3138503SPengcheng.Chen@Sun.COM 			zyd_serial_exit(sc);
3148503SPengcheng.Chen@Sun.COM 		}
3158503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
3168503SPengcheng.Chen@Sun.COM 	}
3178503SPengcheng.Chen@Sun.COM 
3188503SPengcheng.Chen@Sun.COM 	m = allocb(msgdsize(mp) + sizeof (struct zyd_tx_header) + 32,
3198503SPengcheng.Chen@Sun.COM 	    BPRI_MED);
3208503SPengcheng.Chen@Sun.COM 	if (m == NULL) {
3218503SPengcheng.Chen@Sun.COM 		sc->tx_nobuf++;
3228503SPengcheng.Chen@Sun.COM 		(void) zyd_serial_enter(sc, ZYD_NO_SIG);
3238503SPengcheng.Chen@Sun.COM 		sc->resched = B_TRUE;
3248503SPengcheng.Chen@Sun.COM 		zyd_serial_exit(sc);
3258503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
3268503SPengcheng.Chen@Sun.COM 	}
3278503SPengcheng.Chen@Sun.COM 	m->b_rptr += sizeof (struct zyd_tx_header);
3288503SPengcheng.Chen@Sun.COM 	m->b_wptr = m->b_rptr;
3298503SPengcheng.Chen@Sun.COM 
3308503SPengcheng.Chen@Sun.COM 	for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) {
3318503SPengcheng.Chen@Sun.COM 		mblen = MBLKL(m0);
3328503SPengcheng.Chen@Sun.COM 		(void) memcpy(m->b_rptr + off, m0->b_rptr, mblen);
3338503SPengcheng.Chen@Sun.COM 		off += mblen;
3348503SPengcheng.Chen@Sun.COM 	}
3358503SPengcheng.Chen@Sun.COM 	m->b_wptr += off;
3368503SPengcheng.Chen@Sun.COM 
3378503SPengcheng.Chen@Sun.COM 	wh = (struct ieee80211_frame *)m->b_rptr;
3388503SPengcheng.Chen@Sun.COM 	in = ieee80211_find_txnode(ic, wh->i_addr1);
3398503SPengcheng.Chen@Sun.COM 
3408503SPengcheng.Chen@Sun.COM 	if (in == NULL) {
3418503SPengcheng.Chen@Sun.COM 		freemsg(m);
3428503SPengcheng.Chen@Sun.COM 		sc->tx_err++;
3438503SPengcheng.Chen@Sun.COM 		freemsg(mp);
3448503SPengcheng.Chen@Sun.COM 		return (DDI_SUCCESS);
3458503SPengcheng.Chen@Sun.COM 	}
3468503SPengcheng.Chen@Sun.COM 	in->in_inact = 0;
3478503SPengcheng.Chen@Sun.COM 
3488503SPengcheng.Chen@Sun.COM 	if (type == IEEE80211_FC0_TYPE_DATA)
3498503SPengcheng.Chen@Sun.COM 		(void) ieee80211_encap(ic, m, in);
3508503SPengcheng.Chen@Sun.COM 
3518503SPengcheng.Chen@Sun.COM 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
3528503SPengcheng.Chen@Sun.COM 		k = ieee80211_crypto_encap(ic, m);
3538503SPengcheng.Chen@Sun.COM 		if (k == NULL) {
3548503SPengcheng.Chen@Sun.COM 			sc->tx_err++;
3558503SPengcheng.Chen@Sun.COM 			ieee80211_free_node(in);
3568503SPengcheng.Chen@Sun.COM 			freemsg(m);
3578503SPengcheng.Chen@Sun.COM 			freemsg(mp);
3588503SPengcheng.Chen@Sun.COM 			return (DDI_SUCCESS);
3598503SPengcheng.Chen@Sun.COM 		}
3608503SPengcheng.Chen@Sun.COM 		/* packet header may have moved, reset our local pointer */
3618503SPengcheng.Chen@Sun.COM 		wh = (struct ieee80211_frame *)m->b_rptr;
3628503SPengcheng.Chen@Sun.COM 	}
3638503SPengcheng.Chen@Sun.COM 
3648503SPengcheng.Chen@Sun.COM 	/*
3658503SPengcheng.Chen@Sun.COM 	 * pickup a rate. May need work to make adaptive - at present,
3668503SPengcheng.Chen@Sun.COM 	 * picks best rate for mode.
3678503SPengcheng.Chen@Sun.COM 	 */
3688503SPengcheng.Chen@Sun.COM 	if (type == IEEE80211_FC0_TYPE_MGT) {
3698503SPengcheng.Chen@Sun.COM 		/* mgmt frames are sent at 1M */
3708503SPengcheng.Chen@Sun.COM 		rate = (uint16_t)in->in_rates.ir_rates[0];
3718503SPengcheng.Chen@Sun.COM 	} else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
3728503SPengcheng.Chen@Sun.COM 		rate = (uint16_t)ic->ic_sup_rates[ic->ic_curmode].
3738503SPengcheng.Chen@Sun.COM 		    ir_rates[ic->ic_fixed_rate];
3748503SPengcheng.Chen@Sun.COM 	} else {
3758503SPengcheng.Chen@Sun.COM 		rate = (uint16_t)ic->ic_sup_rates[ic->ic_curmode].
3768503SPengcheng.Chen@Sun.COM 		    ir_rates[in->in_txrate];
3778503SPengcheng.Chen@Sun.COM 	}
3788503SPengcheng.Chen@Sun.COM 	rate &= IEEE80211_RATE_VAL;
3798503SPengcheng.Chen@Sun.COM 	if (rate == 0)		/* should not happen */
3808503SPengcheng.Chen@Sun.COM 		rate = 2;
3818503SPengcheng.Chen@Sun.COM 
3828503SPengcheng.Chen@Sun.COM 	/* Get total length of frame */
3838503SPengcheng.Chen@Sun.COM 	len = msgsize(m);
3848503SPengcheng.Chen@Sun.COM 
3858503SPengcheng.Chen@Sun.COM 	m->b_rptr -= sizeof (struct zyd_tx_header);
3868503SPengcheng.Chen@Sun.COM 	buf_hdr = (struct zyd_tx_header *)m->b_rptr;
3878503SPengcheng.Chen@Sun.COM 
3888503SPengcheng.Chen@Sun.COM 	frame_size = (uint16_t)len + 4;	/* include CRC32 */
3898503SPengcheng.Chen@Sun.COM 	buf_hdr->frame_size = LE_16(frame_size);
3908503SPengcheng.Chen@Sun.COM 
3918503SPengcheng.Chen@Sun.COM 	/*
3928503SPengcheng.Chen@Sun.COM 	 * Compute "packet size". What the 10 stands for,
3938503SPengcheng.Chen@Sun.COM 	 * nobody knows.
3948503SPengcheng.Chen@Sun.COM 	 */
3958503SPengcheng.Chen@Sun.COM 	additional_size = sizeof (struct zyd_tx_header) + 10;
3968503SPengcheng.Chen@Sun.COM 	if (sc->mac_rev == ZYD_ZD1211)
3978503SPengcheng.Chen@Sun.COM 		buf_hdr->packet_size = LE_16(frame_size + additional_size);
3988503SPengcheng.Chen@Sun.COM 	else
3998503SPengcheng.Chen@Sun.COM 		buf_hdr->packet_size = LE_16(additional_size);
4008503SPengcheng.Chen@Sun.COM 
4018503SPengcheng.Chen@Sun.COM 	buf_hdr->rate_mod_flags = LE_8(zyd_plcp_signal(rate));
4028503SPengcheng.Chen@Sun.COM 	buf_hdr->type_flags = LE_8(ZYD_TX_FLAG_BACKOFF);
4038503SPengcheng.Chen@Sun.COM 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
4048503SPengcheng.Chen@Sun.COM 		/* multicast frames are not sent at OFDM rates in 802.11b/g */
4058503SPengcheng.Chen@Sun.COM 		if (frame_size > ic->ic_rtsthreshold) {
4068503SPengcheng.Chen@Sun.COM 			buf_hdr->type_flags |= ZYD_TX_FLAG_RTS;
4078503SPengcheng.Chen@Sun.COM 		} else if (ZYD_RATE_IS_OFDM(rate) &&
4088503SPengcheng.Chen@Sun.COM 		    (ic->ic_flags & IEEE80211_F_USEPROT)) {
4098503SPengcheng.Chen@Sun.COM 			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
4108503SPengcheng.Chen@Sun.COM 				buf_hdr->type_flags |=
4118503SPengcheng.Chen@Sun.COM 				    ZYD_TX_FLAG_CTS_TO_SELF;
4128503SPengcheng.Chen@Sun.COM 			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
4138503SPengcheng.Chen@Sun.COM 				buf_hdr->type_flags |= ZYD_TX_FLAG_RTS;
4148503SPengcheng.Chen@Sun.COM 		}
4158503SPengcheng.Chen@Sun.COM 	} else
4168503SPengcheng.Chen@Sun.COM 		buf_hdr->type_flags |= ZYD_TX_FLAG_MULTICAST;
4178503SPengcheng.Chen@Sun.COM 
4188503SPengcheng.Chen@Sun.COM 	if ((type == IEEE80211_FC0_TYPE_CTL) &&
4198503SPengcheng.Chen@Sun.COM 	    (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
4208503SPengcheng.Chen@Sun.COM 	    == IEEE80211_FC0_SUBTYPE_PS_POLL)
4218503SPengcheng.Chen@Sun.COM 		buf_hdr->type_flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);
4228503SPengcheng.Chen@Sun.COM 
4238503SPengcheng.Chen@Sun.COM 	if (ZYD_RATE_IS_OFDM(rate)) {
4248503SPengcheng.Chen@Sun.COM 		buf_hdr->rate_mod_flags |= ZYD_TX_RMF_OFDM;
4258503SPengcheng.Chen@Sun.COM 		if (ic->ic_curmode == IEEE80211_MODE_11A)
4268503SPengcheng.Chen@Sun.COM 			buf_hdr->rate_mod_flags |= ZYD_TX_RMF_5GHZ;
4278503SPengcheng.Chen@Sun.COM 	} else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
4288503SPengcheng.Chen@Sun.COM 		buf_hdr->rate_mod_flags |= ZYD_TX_RMF_SH_PREAMBLE;
4298503SPengcheng.Chen@Sun.COM 
4308503SPengcheng.Chen@Sun.COM 	/*
4318503SPengcheng.Chen@Sun.COM 	 * Compute frame duration and length-extension service flag.
4328503SPengcheng.Chen@Sun.COM 	 */
4338503SPengcheng.Chen@Sun.COM 	service = 0x00;
4348503SPengcheng.Chen@Sun.COM 
4358503SPengcheng.Chen@Sun.COM 	buf_hdr->frame_duration = LE_16((16 * frame_size + rate - 1) / rate);
4368503SPengcheng.Chen@Sun.COM 	buf_hdr->service = service;
4378503SPengcheng.Chen@Sun.COM 	buf_hdr->next_frame_duration = LE_16(0);
4388503SPengcheng.Chen@Sun.COM 
4398503SPengcheng.Chen@Sun.COM 	if (rate == 22) {
4408503SPengcheng.Chen@Sun.COM 		const int remainder = (16 * frame_size) % 22;
4418503SPengcheng.Chen@Sun.COM 		if (remainder != 0 && remainder < 7)
4428503SPengcheng.Chen@Sun.COM 			buf_hdr->service |= ZYD_TX_SERVICE_LENGTH_EXTENSION;
4438503SPengcheng.Chen@Sun.COM 	}
4448503SPengcheng.Chen@Sun.COM 
4458503SPengcheng.Chen@Sun.COM 	res = zyd_usb_send_packet(&sc->usb, m);
4468503SPengcheng.Chen@Sun.COM 	if (res != ZYD_SUCCESS) {
4478503SPengcheng.Chen@Sun.COM 		sc->tx_err++;
4488503SPengcheng.Chen@Sun.COM 	} else {
4498503SPengcheng.Chen@Sun.COM 		(void) zyd_serial_enter(sc, ZYD_NO_SIG);
4508503SPengcheng.Chen@Sun.COM 		sc->tx_queued++;
4518503SPengcheng.Chen@Sun.COM 		zyd_serial_exit(sc);
4528503SPengcheng.Chen@Sun.COM 		freemsg(mp);
4538503SPengcheng.Chen@Sun.COM 		ic->ic_stats.is_tx_frags++;
4548503SPengcheng.Chen@Sun.COM 		ic->ic_stats.is_tx_bytes += len;
4558503SPengcheng.Chen@Sun.COM 	}
4568503SPengcheng.Chen@Sun.COM 
4578503SPengcheng.Chen@Sun.COM 	ieee80211_free_node(in);
4588503SPengcheng.Chen@Sun.COM 
4598503SPengcheng.Chen@Sun.COM 	return (DDI_SUCCESS);
4608503SPengcheng.Chen@Sun.COM }
4618503SPengcheng.Chen@Sun.COM 
4628503SPengcheng.Chen@Sun.COM /*
4638503SPengcheng.Chen@Sun.COM  * Register with the MAC layer.
4648503SPengcheng.Chen@Sun.COM  */
4658503SPengcheng.Chen@Sun.COM static zyd_res
zyd_mac_init(struct zyd_softc * sc)4668503SPengcheng.Chen@Sun.COM zyd_mac_init(struct zyd_softc *sc)
4678503SPengcheng.Chen@Sun.COM {
4688503SPengcheng.Chen@Sun.COM 	struct ieee80211com *ic = &sc->ic;
4698503SPengcheng.Chen@Sun.COM 	mac_register_t *macp;
4708503SPengcheng.Chen@Sun.COM 	wifi_data_t wd = { 0 };
4718503SPengcheng.Chen@Sun.COM 	int err;
4728503SPengcheng.Chen@Sun.COM 
4738503SPengcheng.Chen@Sun.COM 	/*
4748503SPengcheng.Chen@Sun.COM 	 * Initialize mac structure
4758503SPengcheng.Chen@Sun.COM 	 */
4768503SPengcheng.Chen@Sun.COM 	macp = mac_alloc(MAC_VERSION);
4778503SPengcheng.Chen@Sun.COM 	if (macp == NULL) {
4788503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to allocate MAC structure\n");
4798503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
4808503SPengcheng.Chen@Sun.COM 	}
4818503SPengcheng.Chen@Sun.COM 
4828503SPengcheng.Chen@Sun.COM 	/*
4838503SPengcheng.Chen@Sun.COM 	 * Initialize pointer to device specific functions
4848503SPengcheng.Chen@Sun.COM 	 */
4858503SPengcheng.Chen@Sun.COM 	wd.wd_secalloc = WIFI_SEC_NONE;
4868503SPengcheng.Chen@Sun.COM 	wd.wd_opmode = sc->ic.ic_opmode;
4878503SPengcheng.Chen@Sun.COM 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_macaddr);
4888503SPengcheng.Chen@Sun.COM 
4898503SPengcheng.Chen@Sun.COM 	macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI;
4908503SPengcheng.Chen@Sun.COM 	macp->m_driver = sc;
4918503SPengcheng.Chen@Sun.COM 	macp->m_dip = sc->dip;
4928503SPengcheng.Chen@Sun.COM 	macp->m_src_addr = ic->ic_macaddr;
4938503SPengcheng.Chen@Sun.COM 	macp->m_callbacks = &zyd_m_callbacks;
4948503SPengcheng.Chen@Sun.COM 	macp->m_min_sdu = 0;
4958503SPengcheng.Chen@Sun.COM 	macp->m_max_sdu = IEEE80211_MTU;
4968503SPengcheng.Chen@Sun.COM 	macp->m_pdata = &wd;
4978503SPengcheng.Chen@Sun.COM 	macp->m_pdata_size = sizeof (wd);
4988503SPengcheng.Chen@Sun.COM 
4998503SPengcheng.Chen@Sun.COM 	/*
5008503SPengcheng.Chen@Sun.COM 	 * Register the macp to mac
5018503SPengcheng.Chen@Sun.COM 	 */
5028503SPengcheng.Chen@Sun.COM 	err = mac_register(macp, &sc->ic.ic_mach);
5038503SPengcheng.Chen@Sun.COM 	mac_free(macp);
5048503SPengcheng.Chen@Sun.COM 
5058503SPengcheng.Chen@Sun.COM 	if (err != DDI_SUCCESS) {
5068503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to register MAC structure\n");
5078503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
5088503SPengcheng.Chen@Sun.COM 	}
5098503SPengcheng.Chen@Sun.COM 
5108503SPengcheng.Chen@Sun.COM 	return (ZYD_SUCCESS);
5118503SPengcheng.Chen@Sun.COM }
5128503SPengcheng.Chen@Sun.COM 
5138503SPengcheng.Chen@Sun.COM /*
5148503SPengcheng.Chen@Sun.COM  * Register with net80211.
5158503SPengcheng.Chen@Sun.COM  */
5168503SPengcheng.Chen@Sun.COM static void
zyd_wifi_init(struct zyd_softc * sc)5178503SPengcheng.Chen@Sun.COM zyd_wifi_init(struct zyd_softc *sc)
5188503SPengcheng.Chen@Sun.COM {
5198503SPengcheng.Chen@Sun.COM 	struct ieee80211com *ic = &sc->ic;
5208503SPengcheng.Chen@Sun.COM 	int i;
5218503SPengcheng.Chen@Sun.COM 
5228503SPengcheng.Chen@Sun.COM 	/*
5238503SPengcheng.Chen@Sun.COM 	 * Initialize the WiFi part, which will be used by generic layer
5248503SPengcheng.Chen@Sun.COM 	 */
5258503SPengcheng.Chen@Sun.COM 	ic->ic_phytype = IEEE80211_T_OFDM;
5268503SPengcheng.Chen@Sun.COM 	ic->ic_opmode = IEEE80211_M_STA;
5278503SPengcheng.Chen@Sun.COM 	ic->ic_state = IEEE80211_S_INIT;
5288503SPengcheng.Chen@Sun.COM 	ic->ic_maxrssi = 255;
5298503SPengcheng.Chen@Sun.COM 	ic->ic_xmit = zyd_send;
5308503SPengcheng.Chen@Sun.COM 
5318503SPengcheng.Chen@Sun.COM 	/* set device capabilities */
5328503SPengcheng.Chen@Sun.COM 	ic->ic_caps = IEEE80211_C_TXPMGT |	/* tx power management */
5338503SPengcheng.Chen@Sun.COM 	    IEEE80211_C_SHPREAMBLE |		/* short preamble supported */
5348503SPengcheng.Chen@Sun.COM 	    IEEE80211_C_SHSLOT | IEEE80211_C_WPA;	/* Support WPA/WPA2 */
5358503SPengcheng.Chen@Sun.COM 
5368503SPengcheng.Chen@Sun.COM 	/* Copy MAC address */
5378503SPengcheng.Chen@Sun.COM 	IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->macaddr);
5388503SPengcheng.Chen@Sun.COM 
5398503SPengcheng.Chen@Sun.COM 	/*
5408503SPengcheng.Chen@Sun.COM 	 * set supported .11b and .11g rates
5418503SPengcheng.Chen@Sun.COM 	 */
5428503SPengcheng.Chen@Sun.COM 	ic->ic_sup_rates[IEEE80211_MODE_11B] = zyd_rateset_11b;
5438503SPengcheng.Chen@Sun.COM 	ic->ic_sup_rates[IEEE80211_MODE_11G] = zyd_rateset_11g;
5448503SPengcheng.Chen@Sun.COM 
5458503SPengcheng.Chen@Sun.COM 	/*
5468503SPengcheng.Chen@Sun.COM 	 * set supported .11b and .11g channels(1 through 14)
5478503SPengcheng.Chen@Sun.COM 	 */
5488503SPengcheng.Chen@Sun.COM 	for (i = 1; i <= 14; i++) {
5498503SPengcheng.Chen@Sun.COM 		ic->ic_sup_channels[i].ich_freq =
5508503SPengcheng.Chen@Sun.COM 		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
5518503SPengcheng.Chen@Sun.COM 		ic->ic_sup_channels[i].ich_flags =
5528503SPengcheng.Chen@Sun.COM 		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
5538503SPengcheng.Chen@Sun.COM 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
5548503SPengcheng.Chen@Sun.COM 	}
5558503SPengcheng.Chen@Sun.COM 
5568503SPengcheng.Chen@Sun.COM 	/*
5578503SPengcheng.Chen@Sun.COM 	 * Init generic layer (it cannot fail)
5588503SPengcheng.Chen@Sun.COM 	 */
5598503SPengcheng.Chen@Sun.COM 	ieee80211_attach(ic);
5608503SPengcheng.Chen@Sun.COM 
5618503SPengcheng.Chen@Sun.COM 	/* register WPA door */
5628503SPengcheng.Chen@Sun.COM 	ieee80211_register_door(ic, ddi_driver_name(sc->dip),
5638503SPengcheng.Chen@Sun.COM 	    ddi_get_instance(sc->dip));
5648503SPengcheng.Chen@Sun.COM 
5658503SPengcheng.Chen@Sun.COM 	/* Must be after attach! */
5668503SPengcheng.Chen@Sun.COM 	sc->newstate = ic->ic_newstate;
5678503SPengcheng.Chen@Sun.COM 	ic->ic_newstate = zyd_newstate;
5688503SPengcheng.Chen@Sun.COM 
5698503SPengcheng.Chen@Sun.COM 	ieee80211_media_init(ic);
5708503SPengcheng.Chen@Sun.COM 	ic->ic_def_txkey = 0;
5718503SPengcheng.Chen@Sun.COM }
5728503SPengcheng.Chen@Sun.COM 
5738503SPengcheng.Chen@Sun.COM /*
5748503SPengcheng.Chen@Sun.COM  * Device operations
5758503SPengcheng.Chen@Sun.COM  */
5768503SPengcheng.Chen@Sun.COM /*
5778503SPengcheng.Chen@Sun.COM  * Binding the driver to a device.
5788503SPengcheng.Chen@Sun.COM  *
5798503SPengcheng.Chen@Sun.COM  * Concurrency: Until zyd_attach() returns with success,
5808503SPengcheng.Chen@Sun.COM  * the only other entry point that can be executed is getinfo().
5818503SPengcheng.Chen@Sun.COM  * Thus no locking here yet.
5828503SPengcheng.Chen@Sun.COM  */
5838503SPengcheng.Chen@Sun.COM static int
zyd_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5848503SPengcheng.Chen@Sun.COM zyd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5858503SPengcheng.Chen@Sun.COM {
5868503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc;
5878503SPengcheng.Chen@Sun.COM 	char strbuf[32];
5888503SPengcheng.Chen@Sun.COM 	int instance;
5898503SPengcheng.Chen@Sun.COM 	int err;
5908503SPengcheng.Chen@Sun.COM 
5918503SPengcheng.Chen@Sun.COM 	switch (cmd) {
5928503SPengcheng.Chen@Sun.COM 	case DDI_ATTACH:
5938503SPengcheng.Chen@Sun.COM 		break;
5948503SPengcheng.Chen@Sun.COM 
5958503SPengcheng.Chen@Sun.COM 	case DDI_RESUME:
5968503SPengcheng.Chen@Sun.COM 		sc = ddi_get_soft_state(zyd_ssp, ddi_get_instance(dip));
5978503SPengcheng.Chen@Sun.COM 		ASSERT(sc != NULL);
5988503SPengcheng.Chen@Sun.COM 
5998503SPengcheng.Chen@Sun.COM 		(void) zyd_resume(sc);
6008503SPengcheng.Chen@Sun.COM 		return (DDI_SUCCESS);
6018503SPengcheng.Chen@Sun.COM 
6028503SPengcheng.Chen@Sun.COM 	default:
6038503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
6048503SPengcheng.Chen@Sun.COM 	}
6058503SPengcheng.Chen@Sun.COM 
6068503SPengcheng.Chen@Sun.COM 	instance = ddi_get_instance(dip);
6078503SPengcheng.Chen@Sun.COM 	err = ddi_soft_state_zalloc(zyd_ssp, instance);
6088503SPengcheng.Chen@Sun.COM 
6098503SPengcheng.Chen@Sun.COM 	if (err != DDI_SUCCESS) {
6108503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to allocate soft state\n");
6118503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
6128503SPengcheng.Chen@Sun.COM 	}
6138503SPengcheng.Chen@Sun.COM 
6148503SPengcheng.Chen@Sun.COM 	sc = ddi_get_soft_state(zyd_ssp, instance);
6158503SPengcheng.Chen@Sun.COM 	sc->dip = dip;
6168503SPengcheng.Chen@Sun.COM 	sc->timeout_id = 0;
6178503SPengcheng.Chen@Sun.COM 
6188503SPengcheng.Chen@Sun.COM 	if (zyd_usb_init(sc) != ZYD_SUCCESS) {
6198503SPengcheng.Chen@Sun.COM 		ddi_soft_state_free(zyd_ssp, instance);
6208503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
6218503SPengcheng.Chen@Sun.COM 	}
6228503SPengcheng.Chen@Sun.COM 
6238503SPengcheng.Chen@Sun.COM 	if (zyd_hw_init(sc) != ZYD_SUCCESS) {
6248503SPengcheng.Chen@Sun.COM 		zyd_usb_deinit(sc);
6258503SPengcheng.Chen@Sun.COM 		ddi_soft_state_free(zyd_ssp, instance);
6268503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
6278503SPengcheng.Chen@Sun.COM 	}
6288503SPengcheng.Chen@Sun.COM 
6298503SPengcheng.Chen@Sun.COM 	zyd_wifi_init(sc);
6308503SPengcheng.Chen@Sun.COM 
6318503SPengcheng.Chen@Sun.COM 	if (zyd_mac_init(sc) != DDI_SUCCESS) {
6328503SPengcheng.Chen@Sun.COM 		ieee80211_detach(&sc->ic);
6338503SPengcheng.Chen@Sun.COM 		zyd_usb_deinit(sc);
6348503SPengcheng.Chen@Sun.COM 		ddi_soft_state_free(zyd_ssp, instance);
6358503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
6368503SPengcheng.Chen@Sun.COM 	}
6378503SPengcheng.Chen@Sun.COM 
6388503SPengcheng.Chen@Sun.COM 	/*
6398503SPengcheng.Chen@Sun.COM 	 * Create minor node of type DDI_NT_NET_WIFI
6408503SPengcheng.Chen@Sun.COM 	 */
6418503SPengcheng.Chen@Sun.COM 	(void) snprintf(strbuf, sizeof (strbuf), ZYD_DRV_NAME"%d", instance);
6428503SPengcheng.Chen@Sun.COM 	err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
6438503SPengcheng.Chen@Sun.COM 	    instance + 1, DDI_NT_NET_WIFI, 0);
6448503SPengcheng.Chen@Sun.COM 	if (err != DDI_SUCCESS)
6458503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to create minor node\n");
6468503SPengcheng.Chen@Sun.COM 
6478503SPengcheng.Chen@Sun.COM 	/* initialize locking */
6488503SPengcheng.Chen@Sun.COM 	zyd_serial_init(sc);
6498503SPengcheng.Chen@Sun.COM 
6508503SPengcheng.Chen@Sun.COM 	return (DDI_SUCCESS);
6518503SPengcheng.Chen@Sun.COM }
6528503SPengcheng.Chen@Sun.COM 
6538503SPengcheng.Chen@Sun.COM /*
6548503SPengcheng.Chen@Sun.COM  * Detach the driver from a device.
6558503SPengcheng.Chen@Sun.COM  *
6568503SPengcheng.Chen@Sun.COM  * Concurrency: Will be called only after a successful attach
6578503SPengcheng.Chen@Sun.COM  * (and not concurrently).
6588503SPengcheng.Chen@Sun.COM  */
6598503SPengcheng.Chen@Sun.COM static int
zyd_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)6608503SPengcheng.Chen@Sun.COM zyd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
6618503SPengcheng.Chen@Sun.COM {
6628503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = NULL;
6638503SPengcheng.Chen@Sun.COM 
6648503SPengcheng.Chen@Sun.COM 	switch (cmd) {
6658503SPengcheng.Chen@Sun.COM 	case DDI_DETACH:
6668503SPengcheng.Chen@Sun.COM 		break;
6678503SPengcheng.Chen@Sun.COM 
6688503SPengcheng.Chen@Sun.COM 	case DDI_SUSPEND:
6698503SPengcheng.Chen@Sun.COM 		sc = ddi_get_soft_state(zyd_ssp, ddi_get_instance(dip));
6708503SPengcheng.Chen@Sun.COM 		ASSERT(sc != NULL);
6718503SPengcheng.Chen@Sun.COM 
6728503SPengcheng.Chen@Sun.COM 		return (zyd_suspend(sc));
6738503SPengcheng.Chen@Sun.COM 
6748503SPengcheng.Chen@Sun.COM 	default:
6758503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
6768503SPengcheng.Chen@Sun.COM 	}
6778503SPengcheng.Chen@Sun.COM 
6788503SPengcheng.Chen@Sun.COM 	sc = ddi_get_soft_state(zyd_ssp, ddi_get_instance(dip));
6798503SPengcheng.Chen@Sun.COM 	ASSERT(sc != NULL);
6808503SPengcheng.Chen@Sun.COM 
6818503SPengcheng.Chen@Sun.COM 	if (mac_disable(sc->ic.ic_mach) != 0)
6828503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
6838503SPengcheng.Chen@Sun.COM 	/*
6848503SPengcheng.Chen@Sun.COM 	 * Unregister from the MAC layer subsystem
6858503SPengcheng.Chen@Sun.COM 	 */
6868503SPengcheng.Chen@Sun.COM 	(void) mac_unregister(sc->ic.ic_mach);
6878503SPengcheng.Chen@Sun.COM 
6888503SPengcheng.Chen@Sun.COM 	/*
6898503SPengcheng.Chen@Sun.COM 	 * Detach ieee80211
6908503SPengcheng.Chen@Sun.COM 	 */
6918503SPengcheng.Chen@Sun.COM 	ieee80211_detach(&sc->ic);
6928503SPengcheng.Chen@Sun.COM 
6938503SPengcheng.Chen@Sun.COM 	zyd_hw_deinit(sc);
6948503SPengcheng.Chen@Sun.COM 	zyd_usb_deinit(sc);
6958503SPengcheng.Chen@Sun.COM 
6968503SPengcheng.Chen@Sun.COM 	/* At this point it should be safe to release & destroy the locks */
6978503SPengcheng.Chen@Sun.COM 	zyd_serial_deinit(sc);
6988503SPengcheng.Chen@Sun.COM 
6998503SPengcheng.Chen@Sun.COM 	ddi_remove_minor_node(dip, NULL);
7008503SPengcheng.Chen@Sun.COM 	ddi_soft_state_free(zyd_ssp, ddi_get_instance(dip));
7018503SPengcheng.Chen@Sun.COM 	return (DDI_SUCCESS);
7028503SPengcheng.Chen@Sun.COM }
7038503SPengcheng.Chen@Sun.COM 
7048503SPengcheng.Chen@Sun.COM /*
7058503SPengcheng.Chen@Sun.COM  * Mac Call Back functions
7068503SPengcheng.Chen@Sun.COM  */
7078503SPengcheng.Chen@Sun.COM 
7088503SPengcheng.Chen@Sun.COM /*
7098503SPengcheng.Chen@Sun.COM  * Read device statistic information.
7108503SPengcheng.Chen@Sun.COM  */
7118503SPengcheng.Chen@Sun.COM static int
zyd_m_stat(void * arg,uint_t stat,uint64_t * val)7128503SPengcheng.Chen@Sun.COM zyd_m_stat(void *arg, uint_t stat, uint64_t *val)
7138503SPengcheng.Chen@Sun.COM {
7148503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = (struct zyd_softc *)arg;
7158503SPengcheng.Chen@Sun.COM 	ieee80211com_t *ic = &sc->ic;
7168503SPengcheng.Chen@Sun.COM 	ieee80211_node_t *in;
7178503SPengcheng.Chen@Sun.COM 
7188503SPengcheng.Chen@Sun.COM 	switch (stat) {
7198503SPengcheng.Chen@Sun.COM 	case MAC_STAT_IFSPEED:
7208503SPengcheng.Chen@Sun.COM 		if (!sc->usb.connected || sc->suspended || !sc->running)
7218503SPengcheng.Chen@Sun.COM 			return (ENOTSUP);
7228503SPengcheng.Chen@Sun.COM 		in = ieee80211_ref_node(ic->ic_bss);
7238503SPengcheng.Chen@Sun.COM 		*val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
7248503SPengcheng.Chen@Sun.COM 		    IEEE80211_RATE(in->in_txrate) :
7258503SPengcheng.Chen@Sun.COM 		    ic->ic_fixed_rate) / 2 * 1000000;
7268503SPengcheng.Chen@Sun.COM 		ieee80211_free_node(in);
7278503SPengcheng.Chen@Sun.COM 		break;
7288503SPengcheng.Chen@Sun.COM 	case MAC_STAT_NOXMTBUF:
7298503SPengcheng.Chen@Sun.COM 		*val = sc->tx_nobuf;
7308503SPengcheng.Chen@Sun.COM 		break;
7318503SPengcheng.Chen@Sun.COM 	case MAC_STAT_NORCVBUF:
7328503SPengcheng.Chen@Sun.COM 		*val = sc->rx_nobuf;
7338503SPengcheng.Chen@Sun.COM 		break;
7348503SPengcheng.Chen@Sun.COM 	case MAC_STAT_IERRORS:
7358503SPengcheng.Chen@Sun.COM 		*val = sc->rx_err;
7368503SPengcheng.Chen@Sun.COM 		break;
7378503SPengcheng.Chen@Sun.COM 	case MAC_STAT_RBYTES:
7388503SPengcheng.Chen@Sun.COM 		*val = ic->ic_stats.is_rx_bytes;
7398503SPengcheng.Chen@Sun.COM 		break;
7408503SPengcheng.Chen@Sun.COM 	case MAC_STAT_IPACKETS:
7418503SPengcheng.Chen@Sun.COM 		*val = ic->ic_stats.is_rx_frags;
7428503SPengcheng.Chen@Sun.COM 		break;
7438503SPengcheng.Chen@Sun.COM 	case MAC_STAT_OBYTES:
7448503SPengcheng.Chen@Sun.COM 		*val = ic->ic_stats.is_tx_bytes;
7458503SPengcheng.Chen@Sun.COM 		break;
7468503SPengcheng.Chen@Sun.COM 	case MAC_STAT_OPACKETS:
7478503SPengcheng.Chen@Sun.COM 		*val = ic->ic_stats.is_tx_frags;
7488503SPengcheng.Chen@Sun.COM 		break;
7498503SPengcheng.Chen@Sun.COM 	case MAC_STAT_OERRORS:
7508503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_TX_FAILED:
7518503SPengcheng.Chen@Sun.COM 		*val = sc->tx_err;
7528503SPengcheng.Chen@Sun.COM 		break;
7538503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_TX_RETRANS:
7548503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_FCS_ERRORS:
7558503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_WEP_ERRORS:
7568503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_TX_FRAGS:
7578503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_MCAST_TX:
7588503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_RTS_SUCCESS:
7598503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_RTS_FAILURE:
7608503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_ACK_FAILURE:
7618503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_RX_FRAGS:
7628503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_MCAST_RX:
7638503SPengcheng.Chen@Sun.COM 	case WIFI_STAT_RX_DUPS:
7648503SPengcheng.Chen@Sun.COM 		return (ieee80211_stat(ic, stat, val));
7658503SPengcheng.Chen@Sun.COM 	default:
7668503SPengcheng.Chen@Sun.COM 		return (ENOTSUP);
7678503SPengcheng.Chen@Sun.COM 	}
7688503SPengcheng.Chen@Sun.COM 	return (0);
7698503SPengcheng.Chen@Sun.COM }
7708503SPengcheng.Chen@Sun.COM 
7718503SPengcheng.Chen@Sun.COM /*
7728503SPengcheng.Chen@Sun.COM  * Start the device.
7738503SPengcheng.Chen@Sun.COM  *
7748503SPengcheng.Chen@Sun.COM  * Concurrency: Presumably fully concurrent, must lock.
7758503SPengcheng.Chen@Sun.COM  */
7768503SPengcheng.Chen@Sun.COM static int
zyd_m_start(void * arg)7778503SPengcheng.Chen@Sun.COM zyd_m_start(void *arg)
7788503SPengcheng.Chen@Sun.COM {
7798503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = (struct zyd_softc *)arg;
7808503SPengcheng.Chen@Sun.COM 
7818503SPengcheng.Chen@Sun.COM 	(void) zyd_serial_enter(sc, ZYD_NO_SIG);
7828503SPengcheng.Chen@Sun.COM 	if ((!sc->usb.connected) || (zyd_hw_start(sc) != ZYD_SUCCESS)) {
7838503SPengcheng.Chen@Sun.COM 		zyd_serial_exit(sc);
7848503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
7858503SPengcheng.Chen@Sun.COM 	}
7868503SPengcheng.Chen@Sun.COM 	zyd_serial_exit(sc);
7878503SPengcheng.Chen@Sun.COM 
7888503SPengcheng.Chen@Sun.COM 	ieee80211_new_state(&sc->ic, IEEE80211_S_INIT, -1);
7898503SPengcheng.Chen@Sun.COM 	sc->running = B_TRUE;
7908503SPengcheng.Chen@Sun.COM 
7918503SPengcheng.Chen@Sun.COM 	return (DDI_SUCCESS);
7928503SPengcheng.Chen@Sun.COM }
7938503SPengcheng.Chen@Sun.COM 
7948503SPengcheng.Chen@Sun.COM /*
7958503SPengcheng.Chen@Sun.COM  * Stop the device.
7968503SPengcheng.Chen@Sun.COM  */
7978503SPengcheng.Chen@Sun.COM static void
zyd_m_stop(void * arg)7988503SPengcheng.Chen@Sun.COM zyd_m_stop(void *arg)
7998503SPengcheng.Chen@Sun.COM {
8008503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = (struct zyd_softc *)arg;
8018503SPengcheng.Chen@Sun.COM 
8028503SPengcheng.Chen@Sun.COM 	sc->running = B_FALSE;
8038503SPengcheng.Chen@Sun.COM 	ieee80211_new_state(&sc->ic, IEEE80211_S_INIT, -1);
8048503SPengcheng.Chen@Sun.COM 
8058503SPengcheng.Chen@Sun.COM 	(void) zyd_serial_enter(sc, ZYD_NO_SIG);
8068503SPengcheng.Chen@Sun.COM 	sc->resched = B_FALSE;
8078503SPengcheng.Chen@Sun.COM 	zyd_hw_stop(sc);
8088503SPengcheng.Chen@Sun.COM 	zyd_serial_exit(sc);
8098503SPengcheng.Chen@Sun.COM }
8108503SPengcheng.Chen@Sun.COM 
8118503SPengcheng.Chen@Sun.COM /*
8128503SPengcheng.Chen@Sun.COM  * Change the MAC address of the device.
8138503SPengcheng.Chen@Sun.COM  */
8148503SPengcheng.Chen@Sun.COM /*ARGSUSED*/
8158503SPengcheng.Chen@Sun.COM static int
zyd_m_unicst(void * arg,const uint8_t * macaddr)8168503SPengcheng.Chen@Sun.COM zyd_m_unicst(void *arg, const uint8_t *macaddr)
8178503SPengcheng.Chen@Sun.COM {
8188503SPengcheng.Chen@Sun.COM 	return (DDI_FAILURE);
8198503SPengcheng.Chen@Sun.COM }
8208503SPengcheng.Chen@Sun.COM 
8218503SPengcheng.Chen@Sun.COM /*
8228503SPengcheng.Chen@Sun.COM  * Enable/disable multicast.
8238503SPengcheng.Chen@Sun.COM  */
8248503SPengcheng.Chen@Sun.COM /*ARGSUSED*/
8258503SPengcheng.Chen@Sun.COM static int
zyd_m_multicst(void * arg,boolean_t add,const uint8_t * m)8268503SPengcheng.Chen@Sun.COM zyd_m_multicst(void *arg, boolean_t add, const uint8_t *m)
8278503SPengcheng.Chen@Sun.COM {
8288503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_GLD, "multicast not implemented\n"));
8298503SPengcheng.Chen@Sun.COM 	return (DDI_SUCCESS);
8308503SPengcheng.Chen@Sun.COM }
8318503SPengcheng.Chen@Sun.COM 
8328503SPengcheng.Chen@Sun.COM /*
8338503SPengcheng.Chen@Sun.COM  * Enable/disable promiscuous mode.
8348503SPengcheng.Chen@Sun.COM  */
8358503SPengcheng.Chen@Sun.COM /*ARGSUSED*/
8368503SPengcheng.Chen@Sun.COM static int
zyd_m_promisc(void * arg,boolean_t on)8378503SPengcheng.Chen@Sun.COM zyd_m_promisc(void *arg, boolean_t on)
8388503SPengcheng.Chen@Sun.COM {
8398503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_GLD, "promiscuous not implemented\n"));
8408503SPengcheng.Chen@Sun.COM 	return (DDI_SUCCESS);
8418503SPengcheng.Chen@Sun.COM }
8428503SPengcheng.Chen@Sun.COM 
8438503SPengcheng.Chen@Sun.COM /*
8448503SPengcheng.Chen@Sun.COM  * IOCTL request.
8458503SPengcheng.Chen@Sun.COM  */
8468503SPengcheng.Chen@Sun.COM static void
zyd_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)8478503SPengcheng.Chen@Sun.COM zyd_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
8488503SPengcheng.Chen@Sun.COM {
8498503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = (struct zyd_softc *)arg;
8508503SPengcheng.Chen@Sun.COM 	struct ieee80211com *ic = &sc->ic;
8518503SPengcheng.Chen@Sun.COM 
8528503SPengcheng.Chen@Sun.COM 	if (!sc->usb.connected || sc->suspended || !sc->running) {
8538503SPengcheng.Chen@Sun.COM 		miocnak(wq, mp, 0, ENXIO);
8548503SPengcheng.Chen@Sun.COM 		return;
8558503SPengcheng.Chen@Sun.COM 	}
8568503SPengcheng.Chen@Sun.COM 
8578503SPengcheng.Chen@Sun.COM 	if (ieee80211_ioctl(ic, wq, mp) == ENETRESET) {
8588503SPengcheng.Chen@Sun.COM 		if (sc->running && ic->ic_des_esslen) {
8598503SPengcheng.Chen@Sun.COM 			zyd_m_stop(sc);
8608503SPengcheng.Chen@Sun.COM 			if (zyd_m_start(sc) != DDI_SUCCESS)
8618503SPengcheng.Chen@Sun.COM 				return;
8628503SPengcheng.Chen@Sun.COM 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
8638503SPengcheng.Chen@Sun.COM 		}
8648503SPengcheng.Chen@Sun.COM 	}
8658503SPengcheng.Chen@Sun.COM }
8668503SPengcheng.Chen@Sun.COM 
8678503SPengcheng.Chen@Sun.COM /*
8688503SPengcheng.Chen@Sun.COM  * callback functions for /get/set properties
8698503SPengcheng.Chen@Sun.COM  */
8708503SPengcheng.Chen@Sun.COM 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)8718503SPengcheng.Chen@Sun.COM zyd_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
8728503SPengcheng.Chen@Sun.COM     uint_t wldp_length, const void *wldp_buf)
8738503SPengcheng.Chen@Sun.COM {
8748503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = (struct zyd_softc *)arg;
8758503SPengcheng.Chen@Sun.COM 	struct ieee80211com *ic = &sc->ic;
8768503SPengcheng.Chen@Sun.COM 	int err;
8778503SPengcheng.Chen@Sun.COM 
8788503SPengcheng.Chen@Sun.COM 	if (!sc->usb.connected || sc->suspended || !sc->running) {
8798503SPengcheng.Chen@Sun.COM 		return (ENXIO);
8808503SPengcheng.Chen@Sun.COM 	}
8818503SPengcheng.Chen@Sun.COM 
8828503SPengcheng.Chen@Sun.COM 	err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
8838503SPengcheng.Chen@Sun.COM 	    wldp_buf);
8848503SPengcheng.Chen@Sun.COM 	if (err == ENETRESET) {
8858503SPengcheng.Chen@Sun.COM 		if (sc->running && ic->ic_des_esslen) {
8868503SPengcheng.Chen@Sun.COM 			zyd_m_stop(sc);
8878503SPengcheng.Chen@Sun.COM 			if (zyd_m_start(sc) != DDI_SUCCESS)
8888503SPengcheng.Chen@Sun.COM 				return (DDI_FAILURE);
8898503SPengcheng.Chen@Sun.COM 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
8908503SPengcheng.Chen@Sun.COM 		}
8918503SPengcheng.Chen@Sun.COM 		err = 0;
8928503SPengcheng.Chen@Sun.COM 	}
8938503SPengcheng.Chen@Sun.COM 
8948503SPengcheng.Chen@Sun.COM 	return (err);
8958503SPengcheng.Chen@Sun.COM }
8968503SPengcheng.Chen@Sun.COM 
8978503SPengcheng.Chen@Sun.COM 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)8988503SPengcheng.Chen@Sun.COM zyd_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
899*11878SVenu.Iyer@Sun.COM     uint_t wldp_length, void *wldp_buf)
9008503SPengcheng.Chen@Sun.COM {
9018503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = (struct zyd_softc *)arg;
9028503SPengcheng.Chen@Sun.COM 	int err;
9038503SPengcheng.Chen@Sun.COM 
9048503SPengcheng.Chen@Sun.COM 	if (!sc->usb.connected || sc->suspended || !sc->running) {
9058503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
9068503SPengcheng.Chen@Sun.COM 	}
9078503SPengcheng.Chen@Sun.COM 
9088503SPengcheng.Chen@Sun.COM 	err = ieee80211_getprop(&sc->ic, pr_name, wldp_pr_num,
909*11878SVenu.Iyer@Sun.COM 	    wldp_length, wldp_buf);
9108503SPengcheng.Chen@Sun.COM 
9118503SPengcheng.Chen@Sun.COM 	return (err);
9128503SPengcheng.Chen@Sun.COM }
9138503SPengcheng.Chen@Sun.COM 
914*11878SVenu.Iyer@Sun.COM 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*11878SVenu.Iyer@Sun.COM zyd_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
916*11878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t mph)
917*11878SVenu.Iyer@Sun.COM {
918*11878SVenu.Iyer@Sun.COM 	struct zyd_softc *sc = (struct zyd_softc *)arg;
919*11878SVenu.Iyer@Sun.COM 
920*11878SVenu.Iyer@Sun.COM 	ieee80211_propinfo(&sc->ic, pr_name, wldp_pr_num, mph);
921*11878SVenu.Iyer@Sun.COM }
922*11878SVenu.Iyer@Sun.COM 
9238503SPengcheng.Chen@Sun.COM /*
9248503SPengcheng.Chen@Sun.COM  * Transmit a data frame.
9258503SPengcheng.Chen@Sun.COM  */
9268503SPengcheng.Chen@Sun.COM static mblk_t *
zyd_m_tx(void * arg,mblk_t * mp)9278503SPengcheng.Chen@Sun.COM zyd_m_tx(void *arg, mblk_t *mp)
9288503SPengcheng.Chen@Sun.COM {
9298503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = (struct zyd_softc *)arg;
9308503SPengcheng.Chen@Sun.COM 	struct ieee80211com *ic = &sc->ic;
9318503SPengcheng.Chen@Sun.COM 	mblk_t *next;
9328503SPengcheng.Chen@Sun.COM 
9338503SPengcheng.Chen@Sun.COM 	ASSERT(mp != NULL);
9348503SPengcheng.Chen@Sun.COM 
9358503SPengcheng.Chen@Sun.COM 	/* not associated, drop data frames */
9368503SPengcheng.Chen@Sun.COM 	if (ic->ic_state != IEEE80211_S_RUN) {
9378503SPengcheng.Chen@Sun.COM 		freemsg(mp);
9388503SPengcheng.Chen@Sun.COM 		return (DDI_SUCCESS);
9398503SPengcheng.Chen@Sun.COM 	}
9408503SPengcheng.Chen@Sun.COM 
9418503SPengcheng.Chen@Sun.COM 	while (mp != NULL) {
9428503SPengcheng.Chen@Sun.COM 		next = mp->b_next;
9438503SPengcheng.Chen@Sun.COM 		mp->b_next = NULL;
9448503SPengcheng.Chen@Sun.COM 
9458503SPengcheng.Chen@Sun.COM 		if (zyd_send(ic, mp, IEEE80211_FC0_TYPE_DATA) != DDI_SUCCESS) {
9468503SPengcheng.Chen@Sun.COM 			mp->b_next = next;
9478503SPengcheng.Chen@Sun.COM 			break;
9488503SPengcheng.Chen@Sun.COM 		}
9498503SPengcheng.Chen@Sun.COM 		mp = next;
9508503SPengcheng.Chen@Sun.COM 	}
9518503SPengcheng.Chen@Sun.COM 
9528503SPengcheng.Chen@Sun.COM 	return (mp);
9538503SPengcheng.Chen@Sun.COM }
9548503SPengcheng.Chen@Sun.COM 
9558503SPengcheng.Chen@Sun.COM /*
9568503SPengcheng.Chen@Sun.COM  * xxx_newstate callback for net80211.
9578503SPengcheng.Chen@Sun.COM  *
9588503SPengcheng.Chen@Sun.COM  * Called by net80211 whenever the ieee80211 state changes.
9598503SPengcheng.Chen@Sun.COM  */
9608503SPengcheng.Chen@Sun.COM static int
zyd_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)9618503SPengcheng.Chen@Sun.COM zyd_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
9628503SPengcheng.Chen@Sun.COM {
9638503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = ZYD_IC_TO_SOFTC(ic);
9648503SPengcheng.Chen@Sun.COM 	struct ieee80211_node *in;
9658503SPengcheng.Chen@Sun.COM 	uint_t chan;
9668503SPengcheng.Chen@Sun.COM 
9678503SPengcheng.Chen@Sun.COM 	if (sc->timeout_id != 0) {
9688503SPengcheng.Chen@Sun.COM 		(void) untimeout(sc->timeout_id);
9698503SPengcheng.Chen@Sun.COM 		sc->timeout_id = 0;
9708503SPengcheng.Chen@Sun.COM 	}
9718503SPengcheng.Chen@Sun.COM 
9728503SPengcheng.Chen@Sun.COM 	if (!sc->usb.connected || sc->suspended || !sc->running) {
9738503SPengcheng.Chen@Sun.COM 		return (sc->newstate(ic, nstate, arg));
9748503SPengcheng.Chen@Sun.COM 	}
9758503SPengcheng.Chen@Sun.COM 
9768503SPengcheng.Chen@Sun.COM 	switch (nstate) {
9778503SPengcheng.Chen@Sun.COM 	case IEEE80211_S_SCAN:
9788503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_SCAN, "scan timer: starting next\n"));
9798503SPengcheng.Chen@Sun.COM 		sc->timeout_id = timeout(zyd_next_scan, sc,
9808503SPengcheng.Chen@Sun.COM 		    drv_usectohz(ZYD_DWELL_TIME));
9818503SPengcheng.Chen@Sun.COM 		/*FALLTHRU*/
9828503SPengcheng.Chen@Sun.COM 	case IEEE80211_S_AUTH:
9838503SPengcheng.Chen@Sun.COM 	case IEEE80211_S_ASSOC:
9848503SPengcheng.Chen@Sun.COM 	case IEEE80211_S_RUN:
9858503SPengcheng.Chen@Sun.COM 		chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
9868503SPengcheng.Chen@Sun.COM 		if (chan == 0 || chan == IEEE80211_CHAN_ANY) {
9878503SPengcheng.Chen@Sun.COM 			ZYD_WARN("invalid channel number\n");
9888503SPengcheng.Chen@Sun.COM 			return (0);
9898503SPengcheng.Chen@Sun.COM 		}
9908503SPengcheng.Chen@Sun.COM 		(void) zyd_serial_enter(sc, ZYD_SER_SIG);
9918503SPengcheng.Chen@Sun.COM 		zyd_hw_set_channel(sc, chan);
9928503SPengcheng.Chen@Sun.COM 		zyd_serial_exit(sc);
9938503SPengcheng.Chen@Sun.COM 
9948503SPengcheng.Chen@Sun.COM 		in = ic->ic_bss;
9958503SPengcheng.Chen@Sun.COM 		in->in_txrate = in->in_rates.ir_nrates - 1;
9968503SPengcheng.Chen@Sun.COM 	default:
9978503SPengcheng.Chen@Sun.COM 		break;
9988503SPengcheng.Chen@Sun.COM 	}
9998503SPengcheng.Chen@Sun.COM 
10008503SPengcheng.Chen@Sun.COM 	return (sc->newstate(ic, nstate, arg));
10018503SPengcheng.Chen@Sun.COM }
10028503SPengcheng.Chen@Sun.COM 
10038503SPengcheng.Chen@Sun.COM /*
10048503SPengcheng.Chen@Sun.COM  * USB-safe synchronization.
10058503SPengcheng.Chen@Sun.COM  * Debugging routines.
10068503SPengcheng.Chen@Sun.COM  *
10078503SPengcheng.Chen@Sun.COM  * Kmutexes should never be held when making calls to USBA
10088503SPengcheng.Chen@Sun.COM  * or when sleeping. Thus, we implement our own "mutex" on top
10098503SPengcheng.Chen@Sun.COM  * of kmutexes and kcondvars.
10108503SPengcheng.Chen@Sun.COM  *
10118503SPengcheng.Chen@Sun.COM  * Usage: Any (possibly concurrent) access to the soft state or device must
10128503SPengcheng.Chen@Sun.COM  * be serialized with a pair of zyd_serial_enter()/zyd_serial_exit().
10138503SPengcheng.Chen@Sun.COM  */
10148503SPengcheng.Chen@Sun.COM /*
10158503SPengcheng.Chen@Sun.COM  * Initialize the serialization object.
10168503SPengcheng.Chen@Sun.COM  */
10178503SPengcheng.Chen@Sun.COM void
zyd_serial_init(struct zyd_softc * sc)10188503SPengcheng.Chen@Sun.COM zyd_serial_init(struct zyd_softc *sc)
10198503SPengcheng.Chen@Sun.COM {
10208503SPengcheng.Chen@Sun.COM 	mutex_init(&sc->serial.lock, NULL, MUTEX_DRIVER,
10218503SPengcheng.Chen@Sun.COM 	    sc->usb.cdata->dev_iblock_cookie);
10228503SPengcheng.Chen@Sun.COM 	cv_init(&sc->serial.wait, NULL, CV_DRIVER, NULL);
10238503SPengcheng.Chen@Sun.COM 
10248503SPengcheng.Chen@Sun.COM 	sc->serial.held = B_FALSE;
10258503SPengcheng.Chen@Sun.COM 	sc->serial.initialized = B_TRUE;
10268503SPengcheng.Chen@Sun.COM }
10278503SPengcheng.Chen@Sun.COM 
10288503SPengcheng.Chen@Sun.COM /*
10298503SPengcheng.Chen@Sun.COM  * Wait for the serialization object.
10308503SPengcheng.Chen@Sun.COM  *
10318503SPengcheng.Chen@Sun.COM  * If wait_sig is ZYD_SER_SIG, the function may return
10328503SPengcheng.Chen@Sun.COM  * a signal is received. In this case, the serialization object
10338503SPengcheng.Chen@Sun.COM  * is not acquired (but the mutex is) and the return value is ZYD_FAILURE.
10348503SPengcheng.Chen@Sun.COM  *
10358503SPengcheng.Chen@Sun.COM  * In any other case the function returns ZYD_SUCCESS and the
10368503SPengcheng.Chen@Sun.COM  * serialization object is acquired.
10378503SPengcheng.Chen@Sun.COM  */
10388503SPengcheng.Chen@Sun.COM zyd_res
zyd_serial_enter(struct zyd_softc * sc,boolean_t wait_sig)10398503SPengcheng.Chen@Sun.COM zyd_serial_enter(struct zyd_softc *sc, boolean_t wait_sig)
10408503SPengcheng.Chen@Sun.COM {
10418503SPengcheng.Chen@Sun.COM 	zyd_res res;
10428503SPengcheng.Chen@Sun.COM 
10438503SPengcheng.Chen@Sun.COM 	mutex_enter(&sc->serial.lock);
10448503SPengcheng.Chen@Sun.COM 
10458503SPengcheng.Chen@Sun.COM 	res = ZYD_SUCCESS;
10468503SPengcheng.Chen@Sun.COM 
10478503SPengcheng.Chen@Sun.COM 	while (sc->serial.held != B_FALSE) {
10488503SPengcheng.Chen@Sun.COM 		if (wait_sig == ZYD_SER_SIG) {
10498503SPengcheng.Chen@Sun.COM 			res = cv_wait_sig(&sc->serial.wait, &sc->serial.lock);
10508503SPengcheng.Chen@Sun.COM 		} else {
10518503SPengcheng.Chen@Sun.COM 			cv_wait(&sc->serial.wait, &sc->serial.lock);
10528503SPengcheng.Chen@Sun.COM 		}
10538503SPengcheng.Chen@Sun.COM 	}
10548503SPengcheng.Chen@Sun.COM 	sc->serial.held = B_TRUE;
10558503SPengcheng.Chen@Sun.COM 
10568503SPengcheng.Chen@Sun.COM 	mutex_exit(&sc->serial.lock);
10578503SPengcheng.Chen@Sun.COM 
10588503SPengcheng.Chen@Sun.COM 	return (res);
10598503SPengcheng.Chen@Sun.COM }
10608503SPengcheng.Chen@Sun.COM 
10618503SPengcheng.Chen@Sun.COM /*
10628503SPengcheng.Chen@Sun.COM  * Release the serialization object.
10638503SPengcheng.Chen@Sun.COM  */
10648503SPengcheng.Chen@Sun.COM void
zyd_serial_exit(struct zyd_softc * sc)10658503SPengcheng.Chen@Sun.COM zyd_serial_exit(struct zyd_softc *sc)
10668503SPengcheng.Chen@Sun.COM {
10678503SPengcheng.Chen@Sun.COM 	mutex_enter(&sc->serial.lock);
10688503SPengcheng.Chen@Sun.COM 	sc->serial.held = B_FALSE;
10698503SPengcheng.Chen@Sun.COM 	cv_broadcast(&sc->serial.wait);
10708503SPengcheng.Chen@Sun.COM 	mutex_exit(&sc->serial.lock);
10718503SPengcheng.Chen@Sun.COM }
10728503SPengcheng.Chen@Sun.COM 
10738503SPengcheng.Chen@Sun.COM /*
10748503SPengcheng.Chen@Sun.COM  * Destroy the serialization object.
10758503SPengcheng.Chen@Sun.COM  */
10768503SPengcheng.Chen@Sun.COM void
zyd_serial_deinit(struct zyd_softc * sc)10778503SPengcheng.Chen@Sun.COM zyd_serial_deinit(struct zyd_softc *sc)
10788503SPengcheng.Chen@Sun.COM {
10798503SPengcheng.Chen@Sun.COM 	cv_destroy(&sc->serial.wait);
10808503SPengcheng.Chen@Sun.COM 	mutex_destroy(&sc->serial.lock);
10818503SPengcheng.Chen@Sun.COM 
10828503SPengcheng.Chen@Sun.COM 	sc->serial.initialized = B_FALSE;
10838503SPengcheng.Chen@Sun.COM }
10848503SPengcheng.Chen@Sun.COM 
10858503SPengcheng.Chen@Sun.COM 
10868503SPengcheng.Chen@Sun.COM /*
10878503SPengcheng.Chen@Sun.COM  * zyd_cb_lock: a special signal structure that is used for notification
10888503SPengcheng.Chen@Sun.COM  * that a callback function has been called.
10898503SPengcheng.Chen@Sun.COM  */
10908503SPengcheng.Chen@Sun.COM 
10918503SPengcheng.Chen@Sun.COM /* Initializes the zyd_cb_lock structure. */
10928503SPengcheng.Chen@Sun.COM void
zyd_cb_lock_init(struct zyd_cb_lock * lock)10938503SPengcheng.Chen@Sun.COM zyd_cb_lock_init(struct zyd_cb_lock *lock)
10948503SPengcheng.Chen@Sun.COM {
10958503SPengcheng.Chen@Sun.COM 	ASSERT(lock != NULL);
10968503SPengcheng.Chen@Sun.COM 	mutex_init(&lock->mutex, NULL, MUTEX_DRIVER, NULL);
10978503SPengcheng.Chen@Sun.COM 	cv_init(&lock->cv, NULL, CV_DRIVER, NULL);
10988503SPengcheng.Chen@Sun.COM 	lock->done = B_FALSE;
10998503SPengcheng.Chen@Sun.COM }
11008503SPengcheng.Chen@Sun.COM 
11018503SPengcheng.Chen@Sun.COM /* Deinitalizes the zyd_cb_lock structure. */
11028503SPengcheng.Chen@Sun.COM void
zyd_cb_lock_destroy(struct zyd_cb_lock * lock)11038503SPengcheng.Chen@Sun.COM zyd_cb_lock_destroy(struct zyd_cb_lock *lock)
11048503SPengcheng.Chen@Sun.COM {
11058503SPengcheng.Chen@Sun.COM 	ASSERT(lock != NULL);
11068503SPengcheng.Chen@Sun.COM 	mutex_destroy(&lock->mutex);
11078503SPengcheng.Chen@Sun.COM 	cv_destroy(&lock->cv);
11088503SPengcheng.Chen@Sun.COM }
11098503SPengcheng.Chen@Sun.COM 
11108503SPengcheng.Chen@Sun.COM /*
11118503SPengcheng.Chen@Sun.COM  * Wait on lock until someone calls the "signal" function or the timeout
11128503SPengcheng.Chen@Sun.COM  * expires. Note: timeout is in microseconds.
11138503SPengcheng.Chen@Sun.COM  */
11148503SPengcheng.Chen@Sun.COM zyd_res
zyd_cb_lock_wait(struct zyd_cb_lock * lock,clock_t timeout)11158503SPengcheng.Chen@Sun.COM zyd_cb_lock_wait(struct zyd_cb_lock *lock, clock_t timeout)
11168503SPengcheng.Chen@Sun.COM {
11178503SPengcheng.Chen@Sun.COM 	zyd_res res;
11188503SPengcheng.Chen@Sun.COM 	clock_t etime;
11198503SPengcheng.Chen@Sun.COM 	int cv_res;
11208503SPengcheng.Chen@Sun.COM 
11218503SPengcheng.Chen@Sun.COM 	ASSERT(lock != NULL);
11228503SPengcheng.Chen@Sun.COM 
11238503SPengcheng.Chen@Sun.COM 	mutex_enter(&lock->mutex);
11248503SPengcheng.Chen@Sun.COM 
11258503SPengcheng.Chen@Sun.COM 	if (timeout < 0) {
11268503SPengcheng.Chen@Sun.COM 		/* no timeout - wait as long as needed */
11278503SPengcheng.Chen@Sun.COM 		while (lock->done == B_FALSE)
11288503SPengcheng.Chen@Sun.COM 			(void) cv_wait(&lock->cv, &lock->mutex);
11298503SPengcheng.Chen@Sun.COM 	} else {
11308503SPengcheng.Chen@Sun.COM 		/* wait with timeout (given in usec) */
11318503SPengcheng.Chen@Sun.COM 		etime = ddi_get_lbolt() + drv_usectohz(timeout);
11328503SPengcheng.Chen@Sun.COM 		while (lock->done == B_FALSE) {
11338503SPengcheng.Chen@Sun.COM 			cv_res =
11348503SPengcheng.Chen@Sun.COM 			    cv_timedwait_sig(&lock->cv, &lock->mutex, etime);
11358503SPengcheng.Chen@Sun.COM 			if (cv_res <= 0)
11368503SPengcheng.Chen@Sun.COM 				break;
11378503SPengcheng.Chen@Sun.COM 		}
11388503SPengcheng.Chen@Sun.COM 	}
11398503SPengcheng.Chen@Sun.COM 
11408503SPengcheng.Chen@Sun.COM 	res = (lock->done == B_TRUE) ? ZYD_SUCCESS : ZYD_FAILURE;
11418503SPengcheng.Chen@Sun.COM 
11428503SPengcheng.Chen@Sun.COM 	mutex_exit(&lock->mutex);
11438503SPengcheng.Chen@Sun.COM 
11448503SPengcheng.Chen@Sun.COM 	return (res);
11458503SPengcheng.Chen@Sun.COM }
11468503SPengcheng.Chen@Sun.COM 
11478503SPengcheng.Chen@Sun.COM /* Signal that the job (eg. callback) is done and unblock anyone who waits. */
11488503SPengcheng.Chen@Sun.COM void
zyd_cb_lock_signal(struct zyd_cb_lock * lock)11498503SPengcheng.Chen@Sun.COM zyd_cb_lock_signal(struct zyd_cb_lock *lock)
11508503SPengcheng.Chen@Sun.COM {
11518503SPengcheng.Chen@Sun.COM 	ASSERT(lock != NULL);
11528503SPengcheng.Chen@Sun.COM 
11538503SPengcheng.Chen@Sun.COM 	mutex_enter(&lock->mutex);
11548503SPengcheng.Chen@Sun.COM 
11558503SPengcheng.Chen@Sun.COM 	lock->done = B_TRUE;
11568503SPengcheng.Chen@Sun.COM 	cv_broadcast(&lock->cv);
11578503SPengcheng.Chen@Sun.COM 
11588503SPengcheng.Chen@Sun.COM 	mutex_exit(&lock->mutex);
11598503SPengcheng.Chen@Sun.COM }
11608503SPengcheng.Chen@Sun.COM 
11618503SPengcheng.Chen@Sun.COM /*
11628503SPengcheng.Chen@Sun.COM  * Loadable module configuration entry points
11638503SPengcheng.Chen@Sun.COM  */
11648503SPengcheng.Chen@Sun.COM 
11658503SPengcheng.Chen@Sun.COM /*
11668503SPengcheng.Chen@Sun.COM  * _init module entry point.
11678503SPengcheng.Chen@Sun.COM  *
11688503SPengcheng.Chen@Sun.COM  * Called when the module is being loaded into memory.
11698503SPengcheng.Chen@Sun.COM  */
11708503SPengcheng.Chen@Sun.COM int
_init(void)11718503SPengcheng.Chen@Sun.COM _init(void)
11728503SPengcheng.Chen@Sun.COM {
11738503SPengcheng.Chen@Sun.COM 	int err;
11748503SPengcheng.Chen@Sun.COM 
11758503SPengcheng.Chen@Sun.COM 	err = ddi_soft_state_init(&zyd_ssp, sizeof (struct zyd_softc), 1);
11768503SPengcheng.Chen@Sun.COM 
11778503SPengcheng.Chen@Sun.COM 	if (err != DDI_SUCCESS)
11788503SPengcheng.Chen@Sun.COM 		return (err);
11798503SPengcheng.Chen@Sun.COM 
11808503SPengcheng.Chen@Sun.COM 	mac_init_ops(&zyd_devops, ZYD_DRV_NAME);
11818503SPengcheng.Chen@Sun.COM 	err = mod_install(&zyd_ml);
11828503SPengcheng.Chen@Sun.COM 
11838503SPengcheng.Chen@Sun.COM 	if (err != DDI_SUCCESS) {
11848503SPengcheng.Chen@Sun.COM 		mac_fini_ops(&zyd_devops);
11858503SPengcheng.Chen@Sun.COM 		ddi_soft_state_fini(&zyd_ssp);
11868503SPengcheng.Chen@Sun.COM 	}
11878503SPengcheng.Chen@Sun.COM 
11888503SPengcheng.Chen@Sun.COM 	return (err);
11898503SPengcheng.Chen@Sun.COM }
11908503SPengcheng.Chen@Sun.COM 
11918503SPengcheng.Chen@Sun.COM /*
11928503SPengcheng.Chen@Sun.COM  * _info module entry point.
11938503SPengcheng.Chen@Sun.COM  *
11948503SPengcheng.Chen@Sun.COM  * Called to obtain information about the module.
11958503SPengcheng.Chen@Sun.COM  */
11968503SPengcheng.Chen@Sun.COM int
_info(struct modinfo * modinfop)11978503SPengcheng.Chen@Sun.COM _info(struct modinfo *modinfop)
11988503SPengcheng.Chen@Sun.COM {
11998503SPengcheng.Chen@Sun.COM 	return (mod_info(&zyd_ml, modinfop));
12008503SPengcheng.Chen@Sun.COM }
12018503SPengcheng.Chen@Sun.COM 
12028503SPengcheng.Chen@Sun.COM /*
12038503SPengcheng.Chen@Sun.COM  * _fini module entry point.
12048503SPengcheng.Chen@Sun.COM  *
12058503SPengcheng.Chen@Sun.COM  * Called when the module is being unloaded.
12068503SPengcheng.Chen@Sun.COM  */
12078503SPengcheng.Chen@Sun.COM int
_fini(void)12088503SPengcheng.Chen@Sun.COM _fini(void)
12098503SPengcheng.Chen@Sun.COM {
12108503SPengcheng.Chen@Sun.COM 	int err;
12118503SPengcheng.Chen@Sun.COM 
12128503SPengcheng.Chen@Sun.COM 	err = mod_remove(&zyd_ml);
12138503SPengcheng.Chen@Sun.COM 	if (err == DDI_SUCCESS) {
12148503SPengcheng.Chen@Sun.COM 		mac_fini_ops(&zyd_devops);
12158503SPengcheng.Chen@Sun.COM 		ddi_soft_state_fini(&zyd_ssp);
12168503SPengcheng.Chen@Sun.COM 	}
12178503SPengcheng.Chen@Sun.COM 
12188503SPengcheng.Chen@Sun.COM 	return (err);
12198503SPengcheng.Chen@Sun.COM }
1220