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