19113SPengcheng.Chen@Sun.COM /*
2*11878SVenu.Iyer@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
39113SPengcheng.Chen@Sun.COM * Use is subject to license terms.
49113SPengcheng.Chen@Sun.COM */
59113SPengcheng.Chen@Sun.COM
69113SPengcheng.Chen@Sun.COM /*
79113SPengcheng.Chen@Sun.COM * Copyright (c) 2003, 2004
89113SPengcheng.Chen@Sun.COM * Daan Vreeken <Danovitsch@Vitsch.net>. All rights reserved.
99113SPengcheng.Chen@Sun.COM *
109113SPengcheng.Chen@Sun.COM * Redistribution and use in source and binary forms, with or without
119113SPengcheng.Chen@Sun.COM * modification, are permitted provided that the following conditions
129113SPengcheng.Chen@Sun.COM * are met:
139113SPengcheng.Chen@Sun.COM * 1. Redistributions of source code must retain the above copyright
149113SPengcheng.Chen@Sun.COM * notice, this list of conditions and the following disclaimer.
159113SPengcheng.Chen@Sun.COM * 2. Redistributions in binary form must reproduce the above copyright
169113SPengcheng.Chen@Sun.COM * notice, this list of conditions and the following disclaimer in the
179113SPengcheng.Chen@Sun.COM * documentation and/or other materials provided with the distribution.
189113SPengcheng.Chen@Sun.COM * 3. All advertising materials mentioning features or use of this software
199113SPengcheng.Chen@Sun.COM * must display the following acknowledgement:
209113SPengcheng.Chen@Sun.COM * This product includes software developed by Daan Vreeken.
219113SPengcheng.Chen@Sun.COM * 4. Neither the name of the author nor the names of any co-contributors
229113SPengcheng.Chen@Sun.COM * may be used to endorse or promote products derived from this software
239113SPengcheng.Chen@Sun.COM * without specific prior written permission.
249113SPengcheng.Chen@Sun.COM *
259113SPengcheng.Chen@Sun.COM * THIS SOFTWARE IS PROVIDED BY Daan Vreeken AND CONTRIBUTORS ``AS IS'' AND
269113SPengcheng.Chen@Sun.COM * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
279113SPengcheng.Chen@Sun.COM * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
289113SPengcheng.Chen@Sun.COM * ARE DISCLAIMED. IN NO EVENT SHALL Daan Vreeken OR THE VOICES IN HIS HEAD
299113SPengcheng.Chen@Sun.COM * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
309113SPengcheng.Chen@Sun.COM * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
319113SPengcheng.Chen@Sun.COM * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
329113SPengcheng.Chen@Sun.COM * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
339113SPengcheng.Chen@Sun.COM * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
349113SPengcheng.Chen@Sun.COM * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
359113SPengcheng.Chen@Sun.COM * THE POSSIBILITY OF SUCH DAMAGE.
369113SPengcheng.Chen@Sun.COM */
379113SPengcheng.Chen@Sun.COM
389113SPengcheng.Chen@Sun.COM /*
399113SPengcheng.Chen@Sun.COM * Atmel AT76c503 / AT76c503a / AT76c505 / AT76c505a USB WLAN driver
409113SPengcheng.Chen@Sun.COM *
419113SPengcheng.Chen@Sun.COM * Originally written by Daan Vreeken <Danovitsch @ Vitsch . net>
429113SPengcheng.Chen@Sun.COM * http://vitsch.net/bsd/atuwi
439113SPengcheng.Chen@Sun.COM *
449113SPengcheng.Chen@Sun.COM * Contributed to by :
459113SPengcheng.Chen@Sun.COM * Chris Whitehouse, Alistair Phillips, Peter Pilka, Martijn van Buul,
469113SPengcheng.Chen@Sun.COM * Suihong Liang, Arjan van Leeuwen, Stuart Walsh
479113SPengcheng.Chen@Sun.COM *
489113SPengcheng.Chen@Sun.COM * Ported to OpenBSD by Theo de Raadt and David Gwynne.
499113SPengcheng.Chen@Sun.COM */
509113SPengcheng.Chen@Sun.COM
519113SPengcheng.Chen@Sun.COM #include <sys/strsubr.h>
529113SPengcheng.Chen@Sun.COM #include <sys/strsun.h>
539113SPengcheng.Chen@Sun.COM #include <sys/mac_provider.h>
549113SPengcheng.Chen@Sun.COM #include <sys/mac_wifi.h>
559113SPengcheng.Chen@Sun.COM #include <sys/net80211.h>
569113SPengcheng.Chen@Sun.COM #define USBDRV_MAJOR_VER 2
579113SPengcheng.Chen@Sun.COM #define USBDRV_MINOR_VER 0
589113SPengcheng.Chen@Sun.COM #include <sys/usb/usba.h>
599113SPengcheng.Chen@Sun.COM #include <sys/usb/usba/usba_types.h>
609113SPengcheng.Chen@Sun.COM
619113SPengcheng.Chen@Sun.COM #include "fw/atmel_rfmd.hex"
629113SPengcheng.Chen@Sun.COM #include "fw/atmel_rfmd2958.hex"
639113SPengcheng.Chen@Sun.COM #include "fw/atmel_rfmd2958-smc.hex"
649113SPengcheng.Chen@Sun.COM #include "fw/atmel_intersil.hex"
659113SPengcheng.Chen@Sun.COM #include "fw/atmel_at76c505_rfmd.hex"
669113SPengcheng.Chen@Sun.COM #include "fw/atmel_at76c503_rfmd_acc.hex"
679113SPengcheng.Chen@Sun.COM #include "fw/atmel_at76c503_i3863.hex"
689113SPengcheng.Chen@Sun.COM #include "atu.h"
699113SPengcheng.Chen@Sun.COM
709113SPengcheng.Chen@Sun.COM static void *atu_soft_state_p;
719113SPengcheng.Chen@Sun.COM static mac_callbacks_t atu_m_callbacks;
729113SPengcheng.Chen@Sun.COM static const struct ieee80211_rateset atu_rateset = {4, {2, 4, 11, 22}};
739113SPengcheng.Chen@Sun.COM
749113SPengcheng.Chen@Sun.COM static int
atu_usb_request(struct atu_softc * sc,uint8_t type,uint8_t request,uint16_t value,uint16_t index,uint16_t length,uint8_t * data)759113SPengcheng.Chen@Sun.COM atu_usb_request(struct atu_softc *sc, uint8_t type,
769113SPengcheng.Chen@Sun.COM uint8_t request, uint16_t value, uint16_t index, uint16_t length,
779113SPengcheng.Chen@Sun.COM uint8_t *data)
789113SPengcheng.Chen@Sun.COM {
799113SPengcheng.Chen@Sun.COM usb_ctrl_setup_t req;
809113SPengcheng.Chen@Sun.COM usb_cb_flags_t cf;
819113SPengcheng.Chen@Sun.COM usb_cr_t cr;
829113SPengcheng.Chen@Sun.COM mblk_t *mp = NULL;
839113SPengcheng.Chen@Sun.COM int uret = USB_SUCCESS;
849113SPengcheng.Chen@Sun.COM
859113SPengcheng.Chen@Sun.COM bzero(&req, sizeof (req));
869113SPengcheng.Chen@Sun.COM req.bmRequestType = type;
879113SPengcheng.Chen@Sun.COM req.bRequest = request;
889113SPengcheng.Chen@Sun.COM req.wValue = value;
899113SPengcheng.Chen@Sun.COM req.wIndex = index;
909113SPengcheng.Chen@Sun.COM req.wLength = length;
919113SPengcheng.Chen@Sun.COM req.attrs = USB_ATTRS_NONE;
929113SPengcheng.Chen@Sun.COM
939113SPengcheng.Chen@Sun.COM if (type & USB_DEV_REQ_DEV_TO_HOST) {
949113SPengcheng.Chen@Sun.COM req.attrs = USB_ATTRS_AUTOCLEARING;
959113SPengcheng.Chen@Sun.COM uret = usb_pipe_ctrl_xfer_wait(sc->sc_udev->dev_default_ph,
969113SPengcheng.Chen@Sun.COM &req, &mp, &cr, &cf, 0);
979113SPengcheng.Chen@Sun.COM if (mp == NULL)
989113SPengcheng.Chen@Sun.COM return (EIO);
999113SPengcheng.Chen@Sun.COM
1009113SPengcheng.Chen@Sun.COM if (uret == USB_SUCCESS)
1019113SPengcheng.Chen@Sun.COM bcopy(mp->b_rptr, data, length);
1029113SPengcheng.Chen@Sun.COM } else {
1039113SPengcheng.Chen@Sun.COM if ((mp = allocb(length, BPRI_HI)) == NULL)
1049113SPengcheng.Chen@Sun.COM return (ENOMEM);
1059113SPengcheng.Chen@Sun.COM
1069113SPengcheng.Chen@Sun.COM bcopy(data, mp->b_wptr, length);
1079113SPengcheng.Chen@Sun.COM mp->b_wptr += length;
1089113SPengcheng.Chen@Sun.COM uret = usb_pipe_ctrl_xfer_wait(sc->sc_udev->dev_default_ph,
1099113SPengcheng.Chen@Sun.COM &req, &mp, &cr, &cf, 0);
1109113SPengcheng.Chen@Sun.COM }
1119113SPengcheng.Chen@Sun.COM
1129113SPengcheng.Chen@Sun.COM if (mp)
1139113SPengcheng.Chen@Sun.COM freemsg(mp);
1149113SPengcheng.Chen@Sun.COM
1159113SPengcheng.Chen@Sun.COM return (uret == USB_SUCCESS ? 0 : EIO);
1169113SPengcheng.Chen@Sun.COM }
1179113SPengcheng.Chen@Sun.COM
1189113SPengcheng.Chen@Sun.COM static int
atu_get_mib(struct atu_softc * sc,uint8_t type,uint8_t size,uint8_t index,uint8_t * buf)1199113SPengcheng.Chen@Sun.COM atu_get_mib(struct atu_softc *sc, uint8_t type, uint8_t size,
1209113SPengcheng.Chen@Sun.COM uint8_t index, uint8_t *buf)
1219113SPengcheng.Chen@Sun.COM {
1229113SPengcheng.Chen@Sun.COM return atu_usb_request(sc, ATU_VENDOR_IF_IN, 0x033,
1239113SPengcheng.Chen@Sun.COM type << 8, index, size, buf);
1249113SPengcheng.Chen@Sun.COM }
1259113SPengcheng.Chen@Sun.COM
1269113SPengcheng.Chen@Sun.COM static int
atu_get_cmd_status(struct atu_softc * sc,uint8_t cmd,uint8_t * status)1279113SPengcheng.Chen@Sun.COM atu_get_cmd_status(struct atu_softc *sc, uint8_t cmd, uint8_t *status)
1289113SPengcheng.Chen@Sun.COM {
1299113SPengcheng.Chen@Sun.COM /*
1309113SPengcheng.Chen@Sun.COM * all other drivers (including Windoze) request 40 bytes of status
1319113SPengcheng.Chen@Sun.COM * and get a short-xfer of just 6 bytes. we can save 34 bytes of
1329113SPengcheng.Chen@Sun.COM * buffer if we just request those 6 bytes in the first place :)
1339113SPengcheng.Chen@Sun.COM */
1349113SPengcheng.Chen@Sun.COM return atu_usb_request(sc, ATU_VENDOR_IF_IN, 0x22, cmd,
1359113SPengcheng.Chen@Sun.COM 0x0000, 6, status);
1369113SPengcheng.Chen@Sun.COM }
1379113SPengcheng.Chen@Sun.COM
1389113SPengcheng.Chen@Sun.COM static uint8_t
atu_get_dfu_state(struct atu_softc * sc)1399113SPengcheng.Chen@Sun.COM atu_get_dfu_state(struct atu_softc *sc)
1409113SPengcheng.Chen@Sun.COM {
1419113SPengcheng.Chen@Sun.COM uint8_t state;
1429113SPengcheng.Chen@Sun.COM
1439113SPengcheng.Chen@Sun.COM if (atu_usb_request(sc, DFU_GETSTATE, 0, 0, 1, &state))
1449113SPengcheng.Chen@Sun.COM return (DFUState_DFUError);
1459113SPengcheng.Chen@Sun.COM return (state);
1469113SPengcheng.Chen@Sun.COM }
1479113SPengcheng.Chen@Sun.COM
1489113SPengcheng.Chen@Sun.COM static int
atu_get_opmode(struct atu_softc * sc,uint8_t * mode)1499113SPengcheng.Chen@Sun.COM atu_get_opmode(struct atu_softc *sc, uint8_t *mode)
1509113SPengcheng.Chen@Sun.COM {
1519113SPengcheng.Chen@Sun.COM return atu_usb_request(sc, ATU_VENDOR_IF_IN, 0x33, 0x0001,
1529113SPengcheng.Chen@Sun.COM 0x0000, 1, mode);
1539113SPengcheng.Chen@Sun.COM }
1549113SPengcheng.Chen@Sun.COM
1559113SPengcheng.Chen@Sun.COM static int
atu_get_config(struct atu_softc * sc)1569113SPengcheng.Chen@Sun.COM atu_get_config(struct atu_softc *sc)
1579113SPengcheng.Chen@Sun.COM {
1589113SPengcheng.Chen@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
1599113SPengcheng.Chen@Sun.COM struct atu_rfmd_conf rfmd_conf;
1609113SPengcheng.Chen@Sun.COM struct atu_intersil_conf intersil_conf;
1619113SPengcheng.Chen@Sun.COM int err;
1629113SPengcheng.Chen@Sun.COM
1639113SPengcheng.Chen@Sun.COM switch (sc->sc_radio) {
1649113SPengcheng.Chen@Sun.COM case RadioRFMD:
1659113SPengcheng.Chen@Sun.COM case RadioRFMD2958:
1669113SPengcheng.Chen@Sun.COM case RadioRFMD2958_SMC:
1679113SPengcheng.Chen@Sun.COM case AT76C503_RFMD_ACC:
1689113SPengcheng.Chen@Sun.COM case AT76C505_RFMD:
1699113SPengcheng.Chen@Sun.COM err = atu_usb_request(sc, ATU_VENDOR_IF_IN, 0x33, 0x0a02,
1709113SPengcheng.Chen@Sun.COM 0x0000, sizeof (rfmd_conf), (uint8_t *)&rfmd_conf);
1719113SPengcheng.Chen@Sun.COM if (err) {
1729113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: get RFMD config failed\n",
1739113SPengcheng.Chen@Sun.COM sc->sc_name);
1749113SPengcheng.Chen@Sun.COM return (err);
1759113SPengcheng.Chen@Sun.COM }
1769113SPengcheng.Chen@Sun.COM bcopy(rfmd_conf.MACAddr, ic->ic_macaddr, IEEE80211_ADDR_LEN);
1779113SPengcheng.Chen@Sun.COM break;
1789113SPengcheng.Chen@Sun.COM
1799113SPengcheng.Chen@Sun.COM case RadioIntersil:
1809113SPengcheng.Chen@Sun.COM case AT76C503_i3863:
1819113SPengcheng.Chen@Sun.COM err = atu_usb_request(sc, ATU_VENDOR_IF_IN, 0x33, 0x0902,
1829113SPengcheng.Chen@Sun.COM 0x0000, sizeof (intersil_conf), (uint8_t *)&intersil_conf);
1839113SPengcheng.Chen@Sun.COM if (err) {
1849113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: get Intersil config failed\n",
1859113SPengcheng.Chen@Sun.COM sc->sc_name);
1869113SPengcheng.Chen@Sun.COM return (err);
1879113SPengcheng.Chen@Sun.COM }
1889113SPengcheng.Chen@Sun.COM bcopy(intersil_conf.MACAddr, ic->ic_macaddr,
1899113SPengcheng.Chen@Sun.COM IEEE80211_ADDR_LEN);
1909113SPengcheng.Chen@Sun.COM break;
1919113SPengcheng.Chen@Sun.COM }
1929113SPengcheng.Chen@Sun.COM
1939113SPengcheng.Chen@Sun.COM return (0);
1949113SPengcheng.Chen@Sun.COM }
1959113SPengcheng.Chen@Sun.COM
1969113SPengcheng.Chen@Sun.COM static int
atu_wait_completion(struct atu_softc * sc,uint8_t cmd,uint8_t * status)1979113SPengcheng.Chen@Sun.COM atu_wait_completion(struct atu_softc *sc, uint8_t cmd, uint8_t *status)
1989113SPengcheng.Chen@Sun.COM {
1999113SPengcheng.Chen@Sun.COM uint8_t statusreq[6];
2009113SPengcheng.Chen@Sun.COM int idle_count = 0, err;
2019113SPengcheng.Chen@Sun.COM
2029113SPengcheng.Chen@Sun.COM while ((err = atu_get_cmd_status(sc, cmd, statusreq)) == 0) {
2039113SPengcheng.Chen@Sun.COM
2049113SPengcheng.Chen@Sun.COM if ((statusreq[5] != STATUS_IN_PROGRESS) &&
2059113SPengcheng.Chen@Sun.COM (statusreq[5] != STATUS_IDLE)) {
2069113SPengcheng.Chen@Sun.COM if (status != NULL)
2079113SPengcheng.Chen@Sun.COM *status = statusreq[5];
2089113SPengcheng.Chen@Sun.COM return (0);
2099113SPengcheng.Chen@Sun.COM } else if (idle_count++ > 60) {
2109113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: command (0x%02x) timeout\n",
2119113SPengcheng.Chen@Sun.COM sc->sc_name, cmd);
2129113SPengcheng.Chen@Sun.COM return (ETIME);
2139113SPengcheng.Chen@Sun.COM }
2149113SPengcheng.Chen@Sun.COM
2159113SPengcheng.Chen@Sun.COM drv_usecwait(10 * 1000);
2169113SPengcheng.Chen@Sun.COM }
2179113SPengcheng.Chen@Sun.COM
2189113SPengcheng.Chen@Sun.COM return (err);
2199113SPengcheng.Chen@Sun.COM }
2209113SPengcheng.Chen@Sun.COM
2219113SPengcheng.Chen@Sun.COM static int
atu_send_command(struct atu_softc * sc,uint8_t * command,int size)2229113SPengcheng.Chen@Sun.COM atu_send_command(struct atu_softc *sc, uint8_t *command, int size)
2239113SPengcheng.Chen@Sun.COM {
2249113SPengcheng.Chen@Sun.COM return atu_usb_request(sc, ATU_VENDOR_DEV_OUT, 0x0e, 0x0000,
2259113SPengcheng.Chen@Sun.COM 0x0000, size, command);
2269113SPengcheng.Chen@Sun.COM }
2279113SPengcheng.Chen@Sun.COM
2289113SPengcheng.Chen@Sun.COM static int
atu_send_mib(struct atu_softc * sc,uint8_t type,uint8_t size,uint8_t index,void * data)2299113SPengcheng.Chen@Sun.COM atu_send_mib(struct atu_softc *sc, uint8_t type, uint8_t size,
2309113SPengcheng.Chen@Sun.COM uint8_t index, void *data)
2319113SPengcheng.Chen@Sun.COM {
2329113SPengcheng.Chen@Sun.COM struct atu_cmd_set_mib request;
2339113SPengcheng.Chen@Sun.COM int err;
2349113SPengcheng.Chen@Sun.COM
2359113SPengcheng.Chen@Sun.COM bzero(&request, sizeof (request));
2369113SPengcheng.Chen@Sun.COM request.AtCmd = CMD_SET_MIB;
2379113SPengcheng.Chen@Sun.COM request.AtSize = size + 4;
2389113SPengcheng.Chen@Sun.COM request.MIBType = type;
2399113SPengcheng.Chen@Sun.COM request.MIBSize = size;
2409113SPengcheng.Chen@Sun.COM request.MIBIndex = index;
2419113SPengcheng.Chen@Sun.COM request.MIBReserved = 0;
2429113SPengcheng.Chen@Sun.COM
2439113SPengcheng.Chen@Sun.COM /*
2449113SPengcheng.Chen@Sun.COM * For 1 and 2 byte requests we assume a direct value,
2459113SPengcheng.Chen@Sun.COM * everything bigger than 2 bytes we assume a pointer to the data
2469113SPengcheng.Chen@Sun.COM */
2479113SPengcheng.Chen@Sun.COM switch (size) {
2489113SPengcheng.Chen@Sun.COM case 0:
2499113SPengcheng.Chen@Sun.COM break;
2509113SPengcheng.Chen@Sun.COM case 1:
2519113SPengcheng.Chen@Sun.COM request.data[0] = (long)data & 0x000000ff;
2529113SPengcheng.Chen@Sun.COM break;
2539113SPengcheng.Chen@Sun.COM case 2:
2549113SPengcheng.Chen@Sun.COM request.data[0] = (long)data & 0x000000ff;
2559113SPengcheng.Chen@Sun.COM request.data[1] = (long)data >> 8;
2569113SPengcheng.Chen@Sun.COM break;
2579113SPengcheng.Chen@Sun.COM default:
2589113SPengcheng.Chen@Sun.COM bcopy(data, request.data, size);
2599113SPengcheng.Chen@Sun.COM break;
2609113SPengcheng.Chen@Sun.COM }
2619113SPengcheng.Chen@Sun.COM
2629113SPengcheng.Chen@Sun.COM err = atu_usb_request(sc, ATU_VENDOR_DEV_OUT, 0x0e, 0x0000,
2639113SPengcheng.Chen@Sun.COM 0x0000, size+8, (uint8_t *)&request);
2649113SPengcheng.Chen@Sun.COM if (err)
2659113SPengcheng.Chen@Sun.COM return (err);
2669113SPengcheng.Chen@Sun.COM
2679113SPengcheng.Chen@Sun.COM return (atu_wait_completion(sc, CMD_SET_MIB, NULL));
2689113SPengcheng.Chen@Sun.COM }
2699113SPengcheng.Chen@Sun.COM
2709113SPengcheng.Chen@Sun.COM static int
atu_switch_radio(struct atu_softc * sc,boolean_t on)2719113SPengcheng.Chen@Sun.COM atu_switch_radio(struct atu_softc *sc, boolean_t on)
2729113SPengcheng.Chen@Sun.COM {
2739113SPengcheng.Chen@Sun.COM struct atu_cmd radio;
2749113SPengcheng.Chen@Sun.COM boolean_t ostate;
2759113SPengcheng.Chen@Sun.COM int err;
2769113SPengcheng.Chen@Sun.COM
2779113SPengcheng.Chen@Sun.COM /* Intersil doesn't seem to support radio switch */
2789113SPengcheng.Chen@Sun.COM if (sc->sc_radio == RadioIntersil)
2799113SPengcheng.Chen@Sun.COM return (0);
2809113SPengcheng.Chen@Sun.COM
2819113SPengcheng.Chen@Sun.COM ostate = ATU_RADIO_ON(sc) ? B_TRUE : B_FALSE;
2829113SPengcheng.Chen@Sun.COM if (on != ostate) {
2839113SPengcheng.Chen@Sun.COM bzero(&radio, sizeof (radio));
2849113SPengcheng.Chen@Sun.COM radio.Cmd = on ? CMD_RADIO_ON : CMD_RADIO_OFF;
2859113SPengcheng.Chen@Sun.COM
2869113SPengcheng.Chen@Sun.COM err = atu_send_command(sc, (uint8_t *)&radio,
2879113SPengcheng.Chen@Sun.COM sizeof (radio));
2889113SPengcheng.Chen@Sun.COM if (err)
2899113SPengcheng.Chen@Sun.COM return (err);
2909113SPengcheng.Chen@Sun.COM
2919113SPengcheng.Chen@Sun.COM err = atu_wait_completion(sc, radio.Cmd, NULL);
2929113SPengcheng.Chen@Sun.COM if (err)
2939113SPengcheng.Chen@Sun.COM return (err);
2949113SPengcheng.Chen@Sun.COM
2959113SPengcheng.Chen@Sun.COM if (on)
2969113SPengcheng.Chen@Sun.COM sc->sc_flags |= ATU_FLAG_RADIO_ON;
2979113SPengcheng.Chen@Sun.COM else
2989113SPengcheng.Chen@Sun.COM sc->sc_flags &= ~ATU_FLAG_RADIO_ON;
2999113SPengcheng.Chen@Sun.COM }
3009113SPengcheng.Chen@Sun.COM
3019113SPengcheng.Chen@Sun.COM return (0);
3029113SPengcheng.Chen@Sun.COM }
3039113SPengcheng.Chen@Sun.COM
3049113SPengcheng.Chen@Sun.COM static int
atu_config(struct atu_softc * sc)3059113SPengcheng.Chen@Sun.COM atu_config(struct atu_softc *sc)
3069113SPengcheng.Chen@Sun.COM {
3079113SPengcheng.Chen@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
3089113SPengcheng.Chen@Sun.COM struct ieee80211_key *k;
3099113SPengcheng.Chen@Sun.COM struct atu_cmd_card_config cmd;
3109113SPengcheng.Chen@Sun.COM uint8_t rates[4] = {0x82, 0x84, 0x8B, 0x96};
3119113SPengcheng.Chen@Sun.COM int err, i;
3129113SPengcheng.Chen@Sun.COM
3139113SPengcheng.Chen@Sun.COM err = atu_send_mib(sc, MIB_MAC_ADDR_STA, ic->ic_macaddr);
3149113SPengcheng.Chen@Sun.COM if (err) {
3159113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: setting MAC address failed\n",
3169113SPengcheng.Chen@Sun.COM sc->sc_name);
3179113SPengcheng.Chen@Sun.COM return (err);
3189113SPengcheng.Chen@Sun.COM }
3199113SPengcheng.Chen@Sun.COM
3209113SPengcheng.Chen@Sun.COM bzero(&cmd, sizeof (cmd));
3219113SPengcheng.Chen@Sun.COM cmd.Cmd = CMD_STARTUP;
3229113SPengcheng.Chen@Sun.COM cmd.Reserved = 0;
3239113SPengcheng.Chen@Sun.COM cmd.Size = sizeof (cmd) - 4;
3249113SPengcheng.Chen@Sun.COM cmd.Channel = ATU_DEF_CHAN;
3259113SPengcheng.Chen@Sun.COM cmd.ShortRetryLimit = 7;
3269113SPengcheng.Chen@Sun.COM cmd.RTS_Threshold = 2347;
3279113SPengcheng.Chen@Sun.COM cmd.FragThreshold = 2346;
3289113SPengcheng.Chen@Sun.COM cmd.PromiscuousMode = 1;
3299113SPengcheng.Chen@Sun.COM cmd.AutoRateFallback = 1;
3309113SPengcheng.Chen@Sun.COM bcopy(rates, cmd.BasicRateSet, 4);
3319113SPengcheng.Chen@Sun.COM
3329113SPengcheng.Chen@Sun.COM if (ic->ic_flags & IEEE80211_F_PRIVACY) {
3339113SPengcheng.Chen@Sun.COM k = ic->ic_nw_keys + ic->ic_def_txkey;
3349113SPengcheng.Chen@Sun.COM switch (k->wk_keylen) {
3359113SPengcheng.Chen@Sun.COM case 5:
3369113SPengcheng.Chen@Sun.COM cmd.EncryptionType = ATU_ENC_WEP40;
3379113SPengcheng.Chen@Sun.COM break;
3389113SPengcheng.Chen@Sun.COM case 13:
3399113SPengcheng.Chen@Sun.COM cmd.EncryptionType = ATU_ENC_WEP104;
3409113SPengcheng.Chen@Sun.COM break;
3419113SPengcheng.Chen@Sun.COM default:
3429113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: key invalid (%d bytes)\n",
3439113SPengcheng.Chen@Sun.COM sc->sc_name, k->wk_keylen);
3449113SPengcheng.Chen@Sun.COM goto nowep;
3459113SPengcheng.Chen@Sun.COM }
3469113SPengcheng.Chen@Sun.COM cmd.PrivacyInvoked = 1;
3479113SPengcheng.Chen@Sun.COM cmd.ExcludeUnencrypted = 1;
3489113SPengcheng.Chen@Sun.COM cmd.WEP_DefaultKeyID = ic->ic_def_txkey;
3499113SPengcheng.Chen@Sun.COM for (i = 0; i < IEEE80211_WEP_NKID; i++) {
3509113SPengcheng.Chen@Sun.COM k = ic->ic_nw_keys + i;
3519113SPengcheng.Chen@Sun.COM if (k->wk_keylen == 0)
3529113SPengcheng.Chen@Sun.COM continue;
3539113SPengcheng.Chen@Sun.COM bcopy(k->wk_key, cmd.WEP_DefaultKey + i, k->wk_keylen);
3549113SPengcheng.Chen@Sun.COM }
3559113SPengcheng.Chen@Sun.COM } else {
3569113SPengcheng.Chen@Sun.COM nowep:
3579113SPengcheng.Chen@Sun.COM cmd.EncryptionType = ATU_ENC_NONE;
3589113SPengcheng.Chen@Sun.COM }
3599113SPengcheng.Chen@Sun.COM
3609113SPengcheng.Chen@Sun.COM bcopy(ic->ic_des_essid, cmd.SSID, ic->ic_des_esslen);
3619113SPengcheng.Chen@Sun.COM cmd.SSID_Len = ic->ic_des_esslen;
3629113SPengcheng.Chen@Sun.COM cmd.BeaconPeriod = 100;
3639113SPengcheng.Chen@Sun.COM
3649113SPengcheng.Chen@Sun.COM err = atu_send_command(sc, (uint8_t *)&cmd, sizeof (cmd));
3659113SPengcheng.Chen@Sun.COM if (err)
3669113SPengcheng.Chen@Sun.COM return (err);
3679113SPengcheng.Chen@Sun.COM err = atu_wait_completion(sc, CMD_STARTUP, NULL);
3689113SPengcheng.Chen@Sun.COM if (err)
3699113SPengcheng.Chen@Sun.COM return (err);
3709113SPengcheng.Chen@Sun.COM
3719113SPengcheng.Chen@Sun.COM err = atu_switch_radio(sc, B_TRUE);
3729113SPengcheng.Chen@Sun.COM if (err)
3739113SPengcheng.Chen@Sun.COM return (err);
3749113SPengcheng.Chen@Sun.COM
3759113SPengcheng.Chen@Sun.COM err = atu_send_mib(sc, MIB_MAC_MGMT_POWER_MODE,
3769113SPengcheng.Chen@Sun.COM (void *)ATU_POWER_ACTIVE);
3779113SPengcheng.Chen@Sun.COM if (err)
3789113SPengcheng.Chen@Sun.COM return (err);
3799113SPengcheng.Chen@Sun.COM
3809113SPengcheng.Chen@Sun.COM return (0);
3819113SPengcheng.Chen@Sun.COM }
3829113SPengcheng.Chen@Sun.COM
3839113SPengcheng.Chen@Sun.COM static int
atu_start_scan(struct atu_softc * sc)3849113SPengcheng.Chen@Sun.COM atu_start_scan(struct atu_softc *sc)
3859113SPengcheng.Chen@Sun.COM {
3869113SPengcheng.Chen@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
3879113SPengcheng.Chen@Sun.COM struct atu_cmd_do_scan scan;
3889113SPengcheng.Chen@Sun.COM int err;
3899113SPengcheng.Chen@Sun.COM
3909113SPengcheng.Chen@Sun.COM if (!ATU_RUNNING(sc))
3919113SPengcheng.Chen@Sun.COM return (EIO);
3929113SPengcheng.Chen@Sun.COM
3939113SPengcheng.Chen@Sun.COM bzero(&scan, sizeof (scan));
3949113SPengcheng.Chen@Sun.COM scan.Cmd = CMD_START_SCAN;
3959113SPengcheng.Chen@Sun.COM scan.Reserved = 0;
3969113SPengcheng.Chen@Sun.COM scan.Size = sizeof (scan) - 4;
3979113SPengcheng.Chen@Sun.COM (void) memset(scan.BSSID, 0xff, sizeof (scan.BSSID));
3989113SPengcheng.Chen@Sun.COM bcopy(ic->ic_des_essid, scan.SSID, ic->ic_des_esslen);
3999113SPengcheng.Chen@Sun.COM scan.SSID_Len = ic->ic_des_esslen;
4009113SPengcheng.Chen@Sun.COM scan.ScanType = ATU_SCAN_ACTIVE;
4019113SPengcheng.Chen@Sun.COM scan.Channel = ieee80211_chan2ieee(ic, ic->ic_curchan);
4029113SPengcheng.Chen@Sun.COM scan.ProbeDelay = 0;
4039113SPengcheng.Chen@Sun.COM scan.MinChannelTime = 20;
4049113SPengcheng.Chen@Sun.COM scan.MaxChannelTime = 40;
4059113SPengcheng.Chen@Sun.COM scan.InternationalScan = 0;
4069113SPengcheng.Chen@Sun.COM
4079113SPengcheng.Chen@Sun.COM err = atu_send_command(sc, (uint8_t *)&scan, sizeof (scan));
4089113SPengcheng.Chen@Sun.COM if (err) {
4099113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: SCAN command failed\n",
4109113SPengcheng.Chen@Sun.COM sc->sc_name);
4119113SPengcheng.Chen@Sun.COM return (err);
4129113SPengcheng.Chen@Sun.COM }
4139113SPengcheng.Chen@Sun.COM
4149113SPengcheng.Chen@Sun.COM err = atu_wait_completion(sc, CMD_START_SCAN, NULL);
4159113SPengcheng.Chen@Sun.COM if (err) {
4169113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: SCAN completion failed\n",
4179113SPengcheng.Chen@Sun.COM sc->sc_name);
4189113SPengcheng.Chen@Sun.COM return (err);
4199113SPengcheng.Chen@Sun.COM }
4209113SPengcheng.Chen@Sun.COM
4219113SPengcheng.Chen@Sun.COM return (0);
4229113SPengcheng.Chen@Sun.COM }
4239113SPengcheng.Chen@Sun.COM
4249113SPengcheng.Chen@Sun.COM static int
atu_join(struct atu_softc * sc,struct ieee80211_node * node)4259113SPengcheng.Chen@Sun.COM atu_join(struct atu_softc *sc, struct ieee80211_node *node)
4269113SPengcheng.Chen@Sun.COM {
4279113SPengcheng.Chen@Sun.COM struct atu_cmd_join join;
4289113SPengcheng.Chen@Sun.COM uint8_t status;
4299113SPengcheng.Chen@Sun.COM int err;
4309113SPengcheng.Chen@Sun.COM
4319113SPengcheng.Chen@Sun.COM bzero(&join, sizeof (join));
4329113SPengcheng.Chen@Sun.COM join.Cmd = CMD_JOIN;
4339113SPengcheng.Chen@Sun.COM join.Reserved = 0x00;
4349113SPengcheng.Chen@Sun.COM join.Size = sizeof (join) - 4;
4359113SPengcheng.Chen@Sun.COM bcopy(node->in_bssid, join.bssid, IEEE80211_ADDR_LEN);
4369113SPengcheng.Chen@Sun.COM bcopy(node->in_essid, join.essid, node->in_esslen);
4379113SPengcheng.Chen@Sun.COM join.essid_size = node->in_esslen;
4389113SPengcheng.Chen@Sun.COM
4399113SPengcheng.Chen@Sun.COM if (node->in_capinfo & IEEE80211_CAPINFO_IBSS)
4409113SPengcheng.Chen@Sun.COM join.bss_type = ATU_MODE_IBSS;
4419113SPengcheng.Chen@Sun.COM else
4429113SPengcheng.Chen@Sun.COM join.bss_type = ATU_MODE_STA;
4439113SPengcheng.Chen@Sun.COM
4449113SPengcheng.Chen@Sun.COM join.channel = ieee80211_chan2ieee(&sc->sc_ic, node->in_chan);
4459113SPengcheng.Chen@Sun.COM join.timeout = ATU_JOIN_TIMEOUT;
4469113SPengcheng.Chen@Sun.COM join.reserved = 0x00;
4479113SPengcheng.Chen@Sun.COM
4489113SPengcheng.Chen@Sun.COM err = atu_send_command(sc, (uint8_t *)&join, sizeof (join));
4499113SPengcheng.Chen@Sun.COM if (err) {
4509113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: JOIN command failed\n",
4519113SPengcheng.Chen@Sun.COM sc->sc_name);
4529113SPengcheng.Chen@Sun.COM return (err);
4539113SPengcheng.Chen@Sun.COM }
4549113SPengcheng.Chen@Sun.COM err = atu_wait_completion(sc, CMD_JOIN, &status);
4559113SPengcheng.Chen@Sun.COM if (err)
4569113SPengcheng.Chen@Sun.COM return (err);
4579113SPengcheng.Chen@Sun.COM
4589113SPengcheng.Chen@Sun.COM if (status != STATUS_COMPLETE) {
4599113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: incorrect JOIN state (0x%02x)\n",
4609113SPengcheng.Chen@Sun.COM sc->sc_name, status);
4619113SPengcheng.Chen@Sun.COM return (EIO);
4629113SPengcheng.Chen@Sun.COM }
4639113SPengcheng.Chen@Sun.COM
4649113SPengcheng.Chen@Sun.COM return (0);
4659113SPengcheng.Chen@Sun.COM }
4669113SPengcheng.Chen@Sun.COM
4679113SPengcheng.Chen@Sun.COM static int
atu_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)4689113SPengcheng.Chen@Sun.COM atu_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
4699113SPengcheng.Chen@Sun.COM {
4709113SPengcheng.Chen@Sun.COM struct atu_softc *sc = (struct atu_softc *)ic;
4719113SPengcheng.Chen@Sun.COM enum ieee80211_state ostate = ic->ic_state;
4729113SPengcheng.Chen@Sun.COM int err = 0;
4739113SPengcheng.Chen@Sun.COM
4749113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
4759113SPengcheng.Chen@Sun.COM
4769113SPengcheng.Chen@Sun.COM if (sc->sc_scan_timer != 0) {
4779113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
4789113SPengcheng.Chen@Sun.COM (void) untimeout(sc->sc_scan_timer);
4799113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
4809113SPengcheng.Chen@Sun.COM sc->sc_scan_timer = 0;
4819113SPengcheng.Chen@Sun.COM }
4829113SPengcheng.Chen@Sun.COM ostate = ic->ic_state;
4839113SPengcheng.Chen@Sun.COM
4849113SPengcheng.Chen@Sun.COM switch (nstate) {
4859113SPengcheng.Chen@Sun.COM case IEEE80211_S_SCAN:
4869113SPengcheng.Chen@Sun.COM switch (ostate) {
4879113SPengcheng.Chen@Sun.COM case IEEE80211_S_SCAN:
4889113SPengcheng.Chen@Sun.COM case IEEE80211_S_AUTH:
4899113SPengcheng.Chen@Sun.COM case IEEE80211_S_ASSOC:
4909113SPengcheng.Chen@Sun.COM case IEEE80211_S_RUN:
4919113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
4929113SPengcheng.Chen@Sun.COM sc->sc_newstate(ic, nstate, arg);
4939113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
4949113SPengcheng.Chen@Sun.COM if ((err = atu_start_scan(sc)) != 0) {
4959113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
4969113SPengcheng.Chen@Sun.COM ieee80211_cancel_scan(ic);
4979113SPengcheng.Chen@Sun.COM return (err);
4989113SPengcheng.Chen@Sun.COM }
4999113SPengcheng.Chen@Sun.COM sc->sc_scan_timer = timeout(
5009113SPengcheng.Chen@Sun.COM (void (*) (void*))ieee80211_next_scan,
5019113SPengcheng.Chen@Sun.COM (void *)&sc->sc_ic, 0);
5029113SPengcheng.Chen@Sun.COM
5039113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
5049113SPengcheng.Chen@Sun.COM return (err);
5059113SPengcheng.Chen@Sun.COM default:
5069113SPengcheng.Chen@Sun.COM break;
5079113SPengcheng.Chen@Sun.COM }
5089113SPengcheng.Chen@Sun.COM break;
5099113SPengcheng.Chen@Sun.COM
5109113SPengcheng.Chen@Sun.COM case IEEE80211_S_AUTH:
5119113SPengcheng.Chen@Sun.COM switch (ostate) {
5129113SPengcheng.Chen@Sun.COM case IEEE80211_S_INIT:
5139113SPengcheng.Chen@Sun.COM case IEEE80211_S_SCAN:
5149113SPengcheng.Chen@Sun.COM err = atu_join(sc, ic->ic_bss);
5159113SPengcheng.Chen@Sun.COM if (err) {
5169113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
5179113SPengcheng.Chen@Sun.COM return (err);
5189113SPengcheng.Chen@Sun.COM }
5199113SPengcheng.Chen@Sun.COM break;
5209113SPengcheng.Chen@Sun.COM default:
5219113SPengcheng.Chen@Sun.COM break;
5229113SPengcheng.Chen@Sun.COM }
5239113SPengcheng.Chen@Sun.COM default:
5249113SPengcheng.Chen@Sun.COM break;
5259113SPengcheng.Chen@Sun.COM }
5269113SPengcheng.Chen@Sun.COM
5279113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
5289113SPengcheng.Chen@Sun.COM err = sc->sc_newstate(ic, nstate, arg);
5299113SPengcheng.Chen@Sun.COM
5309113SPengcheng.Chen@Sun.COM return (err);
5319113SPengcheng.Chen@Sun.COM }
5329113SPengcheng.Chen@Sun.COM
5339113SPengcheng.Chen@Sun.COM static int
atu_open_pipes(struct atu_softc * sc)5349113SPengcheng.Chen@Sun.COM atu_open_pipes(struct atu_softc *sc)
5359113SPengcheng.Chen@Sun.COM {
5369113SPengcheng.Chen@Sun.COM usb_ep_data_t *ep;
5379113SPengcheng.Chen@Sun.COM usb_pipe_policy_t policy = {0};
5389113SPengcheng.Chen@Sun.COM int uret;
5399113SPengcheng.Chen@Sun.COM
5409113SPengcheng.Chen@Sun.COM ep = usb_lookup_ep_data(sc->sc_dip, sc->sc_udev, 0, 0, 0,
5419113SPengcheng.Chen@Sun.COM USB_EP_ATTR_BULK, USB_EP_DIR_OUT);
5429113SPengcheng.Chen@Sun.COM policy.pp_max_async_reqs = ATU_TX_LIST_CNT;
5439113SPengcheng.Chen@Sun.COM
5449113SPengcheng.Chen@Sun.COM uret = usb_pipe_open(sc->sc_dip, &ep->ep_descr, &policy,
5459113SPengcheng.Chen@Sun.COM USB_FLAGS_SLEEP, &sc->sc_tx_pipe);
5469113SPengcheng.Chen@Sun.COM if (uret != USB_SUCCESS)
5479113SPengcheng.Chen@Sun.COM goto fail;
5489113SPengcheng.Chen@Sun.COM
5499113SPengcheng.Chen@Sun.COM ep = usb_lookup_ep_data(sc->sc_dip, sc->sc_udev, 0, 0, 0,
5509113SPengcheng.Chen@Sun.COM USB_EP_ATTR_BULK, USB_EP_DIR_IN);
5519113SPengcheng.Chen@Sun.COM policy.pp_max_async_reqs = ATU_RX_LIST_CNT + 32;
5529113SPengcheng.Chen@Sun.COM
5539113SPengcheng.Chen@Sun.COM uret = usb_pipe_open(sc->sc_dip, &ep->ep_descr, &policy,
5549113SPengcheng.Chen@Sun.COM USB_FLAGS_SLEEP, &sc->sc_rx_pipe);
5559113SPengcheng.Chen@Sun.COM if (uret != USB_SUCCESS)
5569113SPengcheng.Chen@Sun.COM goto fail;
5579113SPengcheng.Chen@Sun.COM
5589113SPengcheng.Chen@Sun.COM return (0);
5599113SPengcheng.Chen@Sun.COM fail:
5609113SPengcheng.Chen@Sun.COM if (sc->sc_rx_pipe != NULL) {
5619113SPengcheng.Chen@Sun.COM usb_pipe_close(sc->sc_dip, sc->sc_rx_pipe,
5629113SPengcheng.Chen@Sun.COM USB_FLAGS_SLEEP, NULL, 0);
5639113SPengcheng.Chen@Sun.COM sc->sc_rx_pipe = NULL;
5649113SPengcheng.Chen@Sun.COM }
5659113SPengcheng.Chen@Sun.COM
5669113SPengcheng.Chen@Sun.COM if (sc->sc_tx_pipe != NULL) {
5679113SPengcheng.Chen@Sun.COM usb_pipe_close(sc->sc_dip, sc->sc_tx_pipe,
5689113SPengcheng.Chen@Sun.COM USB_FLAGS_SLEEP, NULL, 0);
5699113SPengcheng.Chen@Sun.COM sc->sc_tx_pipe = NULL;
5709113SPengcheng.Chen@Sun.COM }
5719113SPengcheng.Chen@Sun.COM
5729113SPengcheng.Chen@Sun.COM return (EIO);
5739113SPengcheng.Chen@Sun.COM }
5749113SPengcheng.Chen@Sun.COM
5759113SPengcheng.Chen@Sun.COM static void
atu_close_pipes(struct atu_softc * sc)5769113SPengcheng.Chen@Sun.COM atu_close_pipes(struct atu_softc *sc)
5779113SPengcheng.Chen@Sun.COM {
5789113SPengcheng.Chen@Sun.COM usb_flags_t flags = USB_FLAGS_SLEEP;
5799113SPengcheng.Chen@Sun.COM
5809113SPengcheng.Chen@Sun.COM if (sc->sc_rx_pipe != NULL) {
5819113SPengcheng.Chen@Sun.COM usb_pipe_reset(sc->sc_dip, sc->sc_rx_pipe, flags, NULL, 0);
5829113SPengcheng.Chen@Sun.COM usb_pipe_close(sc->sc_dip, sc->sc_rx_pipe, flags, NULL, 0);
5839113SPengcheng.Chen@Sun.COM sc->sc_rx_pipe = NULL;
5849113SPengcheng.Chen@Sun.COM }
5859113SPengcheng.Chen@Sun.COM
5869113SPengcheng.Chen@Sun.COM if (sc->sc_tx_pipe != NULL) {
5879113SPengcheng.Chen@Sun.COM usb_pipe_reset(sc->sc_dip, sc->sc_tx_pipe, flags, NULL, 0);
5889113SPengcheng.Chen@Sun.COM usb_pipe_close(sc->sc_dip, sc->sc_tx_pipe, flags, NULL, 0);
5899113SPengcheng.Chen@Sun.COM sc->sc_tx_pipe = NULL;
5909113SPengcheng.Chen@Sun.COM }
5919113SPengcheng.Chen@Sun.COM }
5929113SPengcheng.Chen@Sun.COM
5939113SPengcheng.Chen@Sun.COM static int atu_rx_trigger(struct atu_softc *sc);
5949113SPengcheng.Chen@Sun.COM
5959113SPengcheng.Chen@Sun.COM /*ARGSUSED*/
5969113SPengcheng.Chen@Sun.COM static void
atu_rxeof(usb_pipe_handle_t pipe,usb_bulk_req_t * req)5979113SPengcheng.Chen@Sun.COM atu_rxeof(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
5989113SPengcheng.Chen@Sun.COM {
5999113SPengcheng.Chen@Sun.COM struct atu_softc *sc = (struct atu_softc *)req->bulk_client_private;
6009113SPengcheng.Chen@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
6019113SPengcheng.Chen@Sun.COM struct ieee80211_node *ni;
6029113SPengcheng.Chen@Sun.COM struct atu_rx_hdr *h;
6039113SPengcheng.Chen@Sun.COM struct ieee80211_frame *wh;
6049113SPengcheng.Chen@Sun.COM mblk_t *mp = req->bulk_data;
6059113SPengcheng.Chen@Sun.COM int len, pktlen;
6069113SPengcheng.Chen@Sun.COM
6079113SPengcheng.Chen@Sun.COM req->bulk_data = NULL;
6089113SPengcheng.Chen@Sun.COM if (req->bulk_completion_reason != USB_CR_OK) {
6099113SPengcheng.Chen@Sun.COM sc->sc_rx_err++;
6109113SPengcheng.Chen@Sun.COM goto fail;
6119113SPengcheng.Chen@Sun.COM }
6129113SPengcheng.Chen@Sun.COM
6139113SPengcheng.Chen@Sun.COM len = msgdsize(mp);
6149113SPengcheng.Chen@Sun.COM if (len < ATU_RX_HDRLEN + ATU_MIN_FRAMELEN) {
6159113SPengcheng.Chen@Sun.COM cmn_err(CE_CONT, "%s: fragment (%d bytes)\n",
6169113SPengcheng.Chen@Sun.COM sc->sc_name, len);
6179113SPengcheng.Chen@Sun.COM sc->sc_rx_err++;
6189113SPengcheng.Chen@Sun.COM goto fail;
6199113SPengcheng.Chen@Sun.COM }
6209113SPengcheng.Chen@Sun.COM
6219113SPengcheng.Chen@Sun.COM h = (struct atu_rx_hdr *)mp->b_rptr;
6229113SPengcheng.Chen@Sun.COM pktlen = h->length - 4;
6239113SPengcheng.Chen@Sun.COM if (pktlen + ATU_RX_HDRLEN + 4 != len) {
6249113SPengcheng.Chen@Sun.COM cmn_err(CE_CONT, "%s: jumbo (%d bytes -> %d bytes)\n",
6259113SPengcheng.Chen@Sun.COM sc->sc_name, len, pktlen);
6269113SPengcheng.Chen@Sun.COM sc->sc_rx_err++;
6279113SPengcheng.Chen@Sun.COM goto fail;
6289113SPengcheng.Chen@Sun.COM }
6299113SPengcheng.Chen@Sun.COM
6309113SPengcheng.Chen@Sun.COM mp->b_rptr += ATU_RX_HDRLEN;
6319113SPengcheng.Chen@Sun.COM mp->b_wptr = mp->b_rptr + pktlen;
6329113SPengcheng.Chen@Sun.COM wh = (struct ieee80211_frame *)mp->b_rptr;
6339113SPengcheng.Chen@Sun.COM
6349113SPengcheng.Chen@Sun.COM if (wh->i_fc[1] & IEEE80211_FC1_WEP)
6359113SPengcheng.Chen@Sun.COM wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
6369113SPengcheng.Chen@Sun.COM
6379113SPengcheng.Chen@Sun.COM ni = ieee80211_find_rxnode(ic, wh);
6389113SPengcheng.Chen@Sun.COM (void) ieee80211_input(ic, mp, ni, h->rssi, h->rx_time);
6399113SPengcheng.Chen@Sun.COM ieee80211_free_node(ni);
6409113SPengcheng.Chen@Sun.COM done:
6419113SPengcheng.Chen@Sun.COM usb_free_bulk_req(req);
6429113SPengcheng.Chen@Sun.COM
6439113SPengcheng.Chen@Sun.COM mutex_enter(&sc->sc_rxlock);
6449113SPengcheng.Chen@Sun.COM sc->rx_queued--;
6459113SPengcheng.Chen@Sun.COM mutex_exit(&sc->sc_rxlock);
6469113SPengcheng.Chen@Sun.COM
6479113SPengcheng.Chen@Sun.COM if (ATU_RUNNING(sc))
6489113SPengcheng.Chen@Sun.COM (void) atu_rx_trigger(sc);
6499113SPengcheng.Chen@Sun.COM return;
6509113SPengcheng.Chen@Sun.COM fail:
6519113SPengcheng.Chen@Sun.COM freemsg(mp);
6529113SPengcheng.Chen@Sun.COM goto done;
6539113SPengcheng.Chen@Sun.COM }
6549113SPengcheng.Chen@Sun.COM
6559113SPengcheng.Chen@Sun.COM /*ARGSUSED*/
6569113SPengcheng.Chen@Sun.COM static void
atu_txeof(usb_pipe_handle_t pipe,usb_bulk_req_t * req)6579113SPengcheng.Chen@Sun.COM atu_txeof(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
6589113SPengcheng.Chen@Sun.COM {
6599113SPengcheng.Chen@Sun.COM struct atu_softc *sc = (struct atu_softc *)req->bulk_client_private;
6609113SPengcheng.Chen@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
6619113SPengcheng.Chen@Sun.COM
6629113SPengcheng.Chen@Sun.COM if (req->bulk_completion_reason != USB_CR_OK)
6639113SPengcheng.Chen@Sun.COM ic->ic_stats.is_tx_failed++;
6649113SPengcheng.Chen@Sun.COM usb_free_bulk_req(req);
6659113SPengcheng.Chen@Sun.COM
6669113SPengcheng.Chen@Sun.COM mutex_enter(&sc->sc_txlock);
6679113SPengcheng.Chen@Sun.COM sc->tx_queued--;
6689113SPengcheng.Chen@Sun.COM
6699113SPengcheng.Chen@Sun.COM if (sc->sc_need_sched) {
6709113SPengcheng.Chen@Sun.COM sc->sc_need_sched = 0;
6719113SPengcheng.Chen@Sun.COM mac_tx_update(ic->ic_mach);
6729113SPengcheng.Chen@Sun.COM }
6739113SPengcheng.Chen@Sun.COM
6749113SPengcheng.Chen@Sun.COM mutex_exit(&sc->sc_txlock);
6759113SPengcheng.Chen@Sun.COM }
6769113SPengcheng.Chen@Sun.COM
6779113SPengcheng.Chen@Sun.COM static int
atu_rx_trigger(struct atu_softc * sc)6789113SPengcheng.Chen@Sun.COM atu_rx_trigger(struct atu_softc *sc)
6799113SPengcheng.Chen@Sun.COM {
6809113SPengcheng.Chen@Sun.COM usb_bulk_req_t *req;
6819113SPengcheng.Chen@Sun.COM int uret;
6829113SPengcheng.Chen@Sun.COM
6839113SPengcheng.Chen@Sun.COM req = usb_alloc_bulk_req(sc->sc_dip, ATU_RX_BUFSZ, USB_FLAGS_SLEEP);
6849113SPengcheng.Chen@Sun.COM if (req == NULL)
6859113SPengcheng.Chen@Sun.COM return (ENOMEM);
6869113SPengcheng.Chen@Sun.COM
6879113SPengcheng.Chen@Sun.COM req->bulk_len = ATU_RX_BUFSZ;
6889113SPengcheng.Chen@Sun.COM req->bulk_client_private = (usb_opaque_t)sc;
6899113SPengcheng.Chen@Sun.COM req->bulk_timeout = 0;
6909113SPengcheng.Chen@Sun.COM req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
6919113SPengcheng.Chen@Sun.COM req->bulk_cb = atu_rxeof;
6929113SPengcheng.Chen@Sun.COM req->bulk_exc_cb = atu_rxeof;
6939113SPengcheng.Chen@Sun.COM req->bulk_completion_reason = 0;
6949113SPengcheng.Chen@Sun.COM req->bulk_cb_flags = 0;
6959113SPengcheng.Chen@Sun.COM
6969113SPengcheng.Chen@Sun.COM uret = usb_pipe_bulk_xfer(sc->sc_rx_pipe, req, 0);
6979113SPengcheng.Chen@Sun.COM if (uret != USB_SUCCESS) {
6989113SPengcheng.Chen@Sun.COM usb_free_bulk_req(req);
6999113SPengcheng.Chen@Sun.COM return (EIO);
7009113SPengcheng.Chen@Sun.COM }
7019113SPengcheng.Chen@Sun.COM
7029113SPengcheng.Chen@Sun.COM mutex_enter(&sc->sc_rxlock);
7039113SPengcheng.Chen@Sun.COM sc->rx_queued++;
7049113SPengcheng.Chen@Sun.COM mutex_exit(&sc->sc_rxlock);
7059113SPengcheng.Chen@Sun.COM
7069113SPengcheng.Chen@Sun.COM return (0);
7079113SPengcheng.Chen@Sun.COM }
7089113SPengcheng.Chen@Sun.COM
7099113SPengcheng.Chen@Sun.COM static int
atu_tx_trigger(struct atu_softc * sc,mblk_t * mp)7109113SPengcheng.Chen@Sun.COM atu_tx_trigger(struct atu_softc *sc, mblk_t *mp)
7119113SPengcheng.Chen@Sun.COM {
7129113SPengcheng.Chen@Sun.COM usb_bulk_req_t *req;
7139113SPengcheng.Chen@Sun.COM int uret;
7149113SPengcheng.Chen@Sun.COM
7159113SPengcheng.Chen@Sun.COM req = usb_alloc_bulk_req(sc->sc_dip, 0, USB_FLAGS_SLEEP);
7169113SPengcheng.Chen@Sun.COM if (req == NULL)
7179113SPengcheng.Chen@Sun.COM return (EIO);
7189113SPengcheng.Chen@Sun.COM
7199113SPengcheng.Chen@Sun.COM req->bulk_len = msgdsize(mp);
7209113SPengcheng.Chen@Sun.COM req->bulk_data = mp;
7219113SPengcheng.Chen@Sun.COM req->bulk_client_private = (usb_opaque_t)sc;
7229113SPengcheng.Chen@Sun.COM req->bulk_timeout = 10;
7239113SPengcheng.Chen@Sun.COM req->bulk_attributes = USB_ATTRS_AUTOCLEARING;
7249113SPengcheng.Chen@Sun.COM req->bulk_cb = atu_txeof;
7259113SPengcheng.Chen@Sun.COM req->bulk_exc_cb = atu_txeof;
7269113SPengcheng.Chen@Sun.COM req->bulk_completion_reason = 0;
7279113SPengcheng.Chen@Sun.COM req->bulk_cb_flags = 0;
7289113SPengcheng.Chen@Sun.COM
7299113SPengcheng.Chen@Sun.COM uret = usb_pipe_bulk_xfer(sc->sc_tx_pipe, req, 0);
7309113SPengcheng.Chen@Sun.COM if (uret != USB_SUCCESS) {
7319113SPengcheng.Chen@Sun.COM req->bulk_data = NULL;
7329113SPengcheng.Chen@Sun.COM usb_free_bulk_req(req);
7339113SPengcheng.Chen@Sun.COM return (EIO);
7349113SPengcheng.Chen@Sun.COM }
7359113SPengcheng.Chen@Sun.COM
7369113SPengcheng.Chen@Sun.COM mutex_enter(&sc->sc_txlock);
7379113SPengcheng.Chen@Sun.COM sc->tx_queued++;
7389113SPengcheng.Chen@Sun.COM mutex_exit(&sc->sc_txlock);
7399113SPengcheng.Chen@Sun.COM
7409113SPengcheng.Chen@Sun.COM return (0);
7419113SPengcheng.Chen@Sun.COM }
7429113SPengcheng.Chen@Sun.COM
7439113SPengcheng.Chen@Sun.COM static int
atu_init_rx_queue(struct atu_softc * sc)7449113SPengcheng.Chen@Sun.COM atu_init_rx_queue(struct atu_softc *sc)
7459113SPengcheng.Chen@Sun.COM {
7469113SPengcheng.Chen@Sun.COM int err, i;
7479113SPengcheng.Chen@Sun.COM
7489113SPengcheng.Chen@Sun.COM mutex_enter(&sc->sc_rxlock);
7499113SPengcheng.Chen@Sun.COM sc->rx_queued = 0;
7509113SPengcheng.Chen@Sun.COM mutex_exit(&sc->sc_rxlock);
7519113SPengcheng.Chen@Sun.COM
7529113SPengcheng.Chen@Sun.COM for (i = 0; i < ATU_RX_LIST_CNT; i++) {
7539113SPengcheng.Chen@Sun.COM err = atu_rx_trigger(sc);
7549113SPengcheng.Chen@Sun.COM if (err)
7559113SPengcheng.Chen@Sun.COM return (err);
7569113SPengcheng.Chen@Sun.COM }
7579113SPengcheng.Chen@Sun.COM
7589113SPengcheng.Chen@Sun.COM return (0);
7599113SPengcheng.Chen@Sun.COM }
7609113SPengcheng.Chen@Sun.COM
7619113SPengcheng.Chen@Sun.COM static void
atu_init_tx_queue(struct atu_softc * sc)7629113SPengcheng.Chen@Sun.COM atu_init_tx_queue(struct atu_softc *sc)
7639113SPengcheng.Chen@Sun.COM {
7649113SPengcheng.Chen@Sun.COM mutex_enter(&sc->sc_txlock);
7659113SPengcheng.Chen@Sun.COM sc->tx_queued = 0;
7669113SPengcheng.Chen@Sun.COM mutex_exit(&sc->sc_txlock);
7679113SPengcheng.Chen@Sun.COM }
7689113SPengcheng.Chen@Sun.COM
7699113SPengcheng.Chen@Sun.COM static int
atu_send(ieee80211com_t * ic,mblk_t * mp,uint8_t type)7709113SPengcheng.Chen@Sun.COM atu_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
7719113SPengcheng.Chen@Sun.COM {
7729113SPengcheng.Chen@Sun.COM struct atu_softc *sc = (struct atu_softc *)ic;
7739113SPengcheng.Chen@Sun.COM struct ieee80211_node *ni = NULL;
7749113SPengcheng.Chen@Sun.COM struct atu_tx_hdr *desc;
7759113SPengcheng.Chen@Sun.COM struct ieee80211_frame *wh;
7769113SPengcheng.Chen@Sun.COM mblk_t *m;
7779113SPengcheng.Chen@Sun.COM int pktlen = msgdsize(mp), err = 0;
7789113SPengcheng.Chen@Sun.COM
7799113SPengcheng.Chen@Sun.COM mutex_enter(&sc->sc_txlock);
7809113SPengcheng.Chen@Sun.COM if (sc->tx_queued > ATU_TX_LIST_CNT) {
7819113SPengcheng.Chen@Sun.COM sc->sc_tx_nobuf++;
7829113SPengcheng.Chen@Sun.COM mutex_exit(&sc->sc_txlock);
7839113SPengcheng.Chen@Sun.COM err = ENOMEM;
7849113SPengcheng.Chen@Sun.COM goto fail;
7859113SPengcheng.Chen@Sun.COM }
7869113SPengcheng.Chen@Sun.COM mutex_exit(&sc->sc_txlock);
7879113SPengcheng.Chen@Sun.COM
7889113SPengcheng.Chen@Sun.COM m = allocb(ATU_TX_BUFSZ, BPRI_MED);
7899113SPengcheng.Chen@Sun.COM if (m == NULL) {
7909113SPengcheng.Chen@Sun.COM sc->sc_tx_nobuf++;
7919113SPengcheng.Chen@Sun.COM err = ENOMEM;
7929113SPengcheng.Chen@Sun.COM goto fail;
7939113SPengcheng.Chen@Sun.COM }
7949113SPengcheng.Chen@Sun.COM /* reserve tx header space */
7959113SPengcheng.Chen@Sun.COM m->b_rptr += ATU_TX_HDRLEN;
7969113SPengcheng.Chen@Sun.COM m->b_wptr += ATU_TX_HDRLEN;
7979113SPengcheng.Chen@Sun.COM
7989113SPengcheng.Chen@Sun.COM /* copy and (implicitly) free old data */
7999113SPengcheng.Chen@Sun.COM mcopymsg(mp, m->b_wptr);
8009113SPengcheng.Chen@Sun.COM m->b_wptr += pktlen;
8019113SPengcheng.Chen@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr;
8029113SPengcheng.Chen@Sun.COM
8039113SPengcheng.Chen@Sun.COM ni = ieee80211_find_txnode(ic, wh->i_addr1);
8049113SPengcheng.Chen@Sun.COM if (ni == NULL) {
8059113SPengcheng.Chen@Sun.COM ic->ic_stats.is_tx_failed++;
8069113SPengcheng.Chen@Sun.COM freemsg(m);
8079113SPengcheng.Chen@Sun.COM err = ENXIO;
8089113SPengcheng.Chen@Sun.COM goto fail;
8099113SPengcheng.Chen@Sun.COM }
8109113SPengcheng.Chen@Sun.COM
8119113SPengcheng.Chen@Sun.COM if (type == IEEE80211_FC0_TYPE_DATA)
8129113SPengcheng.Chen@Sun.COM (void) ieee80211_encap(ic, m, ni);
8139113SPengcheng.Chen@Sun.COM
8149113SPengcheng.Chen@Sun.COM /* full WEP in device, prune WEP fields (IV, KID) */
8159113SPengcheng.Chen@Sun.COM if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
8169113SPengcheng.Chen@Sun.COM (void) memmove(m->b_rptr + IEEE80211_WEP_IVLEN
8179113SPengcheng.Chen@Sun.COM + IEEE80211_WEP_KIDLEN, m->b_rptr,
8189113SPengcheng.Chen@Sun.COM sizeof (struct ieee80211_frame));
8199113SPengcheng.Chen@Sun.COM m->b_rptr += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
8209113SPengcheng.Chen@Sun.COM }
8219113SPengcheng.Chen@Sun.COM pktlen = msgdsize(m);
8229113SPengcheng.Chen@Sun.COM m->b_rptr -= ATU_TX_HDRLEN;
8239113SPengcheng.Chen@Sun.COM
8249113SPengcheng.Chen@Sun.COM /* setup tx header */
8259113SPengcheng.Chen@Sun.COM desc = (struct atu_tx_hdr *)m->b_rptr;
8269113SPengcheng.Chen@Sun.COM bzero(desc, ATU_TX_HDRLEN);
8279113SPengcheng.Chen@Sun.COM desc->length = (uint16_t)pktlen;
8289113SPengcheng.Chen@Sun.COM desc->tx_rate = ATU_DEF_TX_RATE;
8299113SPengcheng.Chen@Sun.COM
8309113SPengcheng.Chen@Sun.COM err = atu_tx_trigger(sc, m);
8319113SPengcheng.Chen@Sun.COM if (!err) {
8329113SPengcheng.Chen@Sun.COM ic->ic_stats.is_tx_frags++;
8339113SPengcheng.Chen@Sun.COM ic->ic_stats.is_tx_bytes += pktlen;
8349113SPengcheng.Chen@Sun.COM } else {
8359113SPengcheng.Chen@Sun.COM ic->ic_stats.is_tx_failed++;
8369113SPengcheng.Chen@Sun.COM freemsg(m);
8379113SPengcheng.Chen@Sun.COM }
8389113SPengcheng.Chen@Sun.COM fail:
8399113SPengcheng.Chen@Sun.COM if (ni != NULL)
8409113SPengcheng.Chen@Sun.COM ieee80211_free_node(ni);
8419113SPengcheng.Chen@Sun.COM
8429113SPengcheng.Chen@Sun.COM return (err);
8439113SPengcheng.Chen@Sun.COM }
8449113SPengcheng.Chen@Sun.COM
8459113SPengcheng.Chen@Sun.COM static int
atu_stop(struct atu_softc * sc)8469113SPengcheng.Chen@Sun.COM atu_stop(struct atu_softc *sc)
8479113SPengcheng.Chen@Sun.COM {
8489113SPengcheng.Chen@Sun.COM sc->sc_flags &= ~ATU_FLAG_RUNNING;
8499113SPengcheng.Chen@Sun.COM atu_close_pipes(sc);
8509113SPengcheng.Chen@Sun.COM
8519113SPengcheng.Chen@Sun.COM return (atu_switch_radio(sc, B_FALSE));
8529113SPengcheng.Chen@Sun.COM }
8539113SPengcheng.Chen@Sun.COM
8549113SPengcheng.Chen@Sun.COM static int
atu_init(struct atu_softc * sc)8559113SPengcheng.Chen@Sun.COM atu_init(struct atu_softc *sc)
8569113SPengcheng.Chen@Sun.COM {
8579113SPengcheng.Chen@Sun.COM int err;
8589113SPengcheng.Chen@Sun.COM
8599113SPengcheng.Chen@Sun.COM err = atu_stop(sc);
8609113SPengcheng.Chen@Sun.COM if (err)
8619113SPengcheng.Chen@Sun.COM return (err);
8629113SPengcheng.Chen@Sun.COM
8639113SPengcheng.Chen@Sun.COM err = atu_open_pipes(sc);
8649113SPengcheng.Chen@Sun.COM if (err)
8659113SPengcheng.Chen@Sun.COM goto fail;
8669113SPengcheng.Chen@Sun.COM
8679113SPengcheng.Chen@Sun.COM err = atu_config(sc);
8689113SPengcheng.Chen@Sun.COM if (err) {
8699113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: startup config failed\n",
8709113SPengcheng.Chen@Sun.COM sc->sc_name);
8719113SPengcheng.Chen@Sun.COM goto fail;
8729113SPengcheng.Chen@Sun.COM }
8739113SPengcheng.Chen@Sun.COM
8749113SPengcheng.Chen@Sun.COM atu_init_tx_queue(sc);
8759113SPengcheng.Chen@Sun.COM
8769113SPengcheng.Chen@Sun.COM err = atu_init_rx_queue(sc);
8779113SPengcheng.Chen@Sun.COM if (err) {
8789113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: rx queue init failed\n", sc->sc_name);
8799113SPengcheng.Chen@Sun.COM goto fail;
8809113SPengcheng.Chen@Sun.COM }
8819113SPengcheng.Chen@Sun.COM
8829113SPengcheng.Chen@Sun.COM sc->sc_flags |= ATU_FLAG_RUNNING;
8839113SPengcheng.Chen@Sun.COM
8849113SPengcheng.Chen@Sun.COM return (0);
8859113SPengcheng.Chen@Sun.COM fail:
8869113SPengcheng.Chen@Sun.COM (void) atu_stop(sc);
8879113SPengcheng.Chen@Sun.COM return (err);
8889113SPengcheng.Chen@Sun.COM }
8899113SPengcheng.Chen@Sun.COM
8909113SPengcheng.Chen@Sun.COM static void
atu_watchdog(void * arg)8919113SPengcheng.Chen@Sun.COM atu_watchdog(void *arg)
8929113SPengcheng.Chen@Sun.COM {
8939113SPengcheng.Chen@Sun.COM struct atu_softc *sc = arg;
8949113SPengcheng.Chen@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
8959113SPengcheng.Chen@Sun.COM
8969113SPengcheng.Chen@Sun.COM ieee80211_stop_watchdog(ic);
8979113SPengcheng.Chen@Sun.COM
8989113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
8999113SPengcheng.Chen@Sun.COM if (!ATU_RUNNING(sc)) {
9009113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
9019113SPengcheng.Chen@Sun.COM return;
9029113SPengcheng.Chen@Sun.COM }
9039113SPengcheng.Chen@Sun.COM
9049113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
9059113SPengcheng.Chen@Sun.COM switch (ic->ic_state) {
9069113SPengcheng.Chen@Sun.COM case IEEE80211_S_AUTH:
9079113SPengcheng.Chen@Sun.COM case IEEE80211_S_ASSOC:
9089113SPengcheng.Chen@Sun.COM if (ic->ic_bss->in_fails > 0)
9099113SPengcheng.Chen@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
9109113SPengcheng.Chen@Sun.COM else
9119113SPengcheng.Chen@Sun.COM ieee80211_watchdog(ic);
9129113SPengcheng.Chen@Sun.COM break;
9139113SPengcheng.Chen@Sun.COM }
9149113SPengcheng.Chen@Sun.COM }
9159113SPengcheng.Chen@Sun.COM
9169113SPengcheng.Chen@Sun.COM static int
atu_dfu_stage1(void * arg)9179113SPengcheng.Chen@Sun.COM atu_dfu_stage1(void *arg)
9189113SPengcheng.Chen@Sun.COM {
9199113SPengcheng.Chen@Sun.COM struct atu_softc *sc = arg;
9209113SPengcheng.Chen@Sun.COM uint8_t state, *ptr = NULL, status[6];
9219113SPengcheng.Chen@Sun.COM int block_size, bytes_left = 0, block = 0, err, i, count = 0;
9229113SPengcheng.Chen@Sun.COM
9239113SPengcheng.Chen@Sun.COM /*
9249113SPengcheng.Chen@Sun.COM * Uploading firmware is done with the DFU (Device Firmware Upgrade)
9259113SPengcheng.Chen@Sun.COM * interface. See "Universal Serial Bus - Device Class Specification
9269113SPengcheng.Chen@Sun.COM * for Device Firmware Upgrade" pdf for details of the protocol.
9279113SPengcheng.Chen@Sun.COM * Maybe this could be moved to a separate 'firmware driver' once more
9289113SPengcheng.Chen@Sun.COM * device drivers need it... For now we'll just do it here.
9299113SPengcheng.Chen@Sun.COM *
9309113SPengcheng.Chen@Sun.COM * Just for your information, the Atmel's DFU descriptor looks like
9319113SPengcheng.Chen@Sun.COM * this:
9329113SPengcheng.Chen@Sun.COM *
9339113SPengcheng.Chen@Sun.COM * 07 size
9349113SPengcheng.Chen@Sun.COM * 21 type
9359113SPengcheng.Chen@Sun.COM * 01 capabilities : only firmware download, *need* reset
9369113SPengcheng.Chen@Sun.COM * after download
9379113SPengcheng.Chen@Sun.COM * 13 05 detach timeout : max 1299ms between DFU_DETACH and
9389113SPengcheng.Chen@Sun.COM * reset
9399113SPengcheng.Chen@Sun.COM * 00 04 max bytes of firmware per transaction : 1024
9409113SPengcheng.Chen@Sun.COM */
9419113SPengcheng.Chen@Sun.COM for (i = 0; i < sizeof (atu_fw_table) / sizeof (atu_fw_table[0]); i++)
9429113SPengcheng.Chen@Sun.COM if (sc->sc_radio == atu_fw_table[i].atur_type) {
9439113SPengcheng.Chen@Sun.COM ptr = atu_fw_table[i].atur_int;
9449113SPengcheng.Chen@Sun.COM bytes_left = atu_fw_table[i].atur_int_size;
9459113SPengcheng.Chen@Sun.COM }
9469113SPengcheng.Chen@Sun.COM
9479113SPengcheng.Chen@Sun.COM state = atu_get_dfu_state(sc);
9489113SPengcheng.Chen@Sun.COM while (block >= 0 && state > 0) {
9499113SPengcheng.Chen@Sun.COM switch (state) {
9509113SPengcheng.Chen@Sun.COM case DFUState_DnLoadSync:
9519113SPengcheng.Chen@Sun.COM /* get DFU status */
9529113SPengcheng.Chen@Sun.COM err = atu_usb_request(sc, DFU_GETSTATUS, 0, 0, 6,
9539113SPengcheng.Chen@Sun.COM status);
9549113SPengcheng.Chen@Sun.COM if (err) {
9559113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: DFU get status failed\n",
9569113SPengcheng.Chen@Sun.COM sc->sc_name);
9579113SPengcheng.Chen@Sun.COM return (err);
9589113SPengcheng.Chen@Sun.COM }
9599113SPengcheng.Chen@Sun.COM /* success means state => DnLoadIdle */
9609113SPengcheng.Chen@Sun.COM state = DFUState_DnLoadIdle;
9619113SPengcheng.Chen@Sun.COM continue;
9629113SPengcheng.Chen@Sun.COM
9639113SPengcheng.Chen@Sun.COM case DFUState_DFUIdle:
9649113SPengcheng.Chen@Sun.COM case DFUState_DnLoadIdle:
9659113SPengcheng.Chen@Sun.COM if (bytes_left >= DFU_MaxBlockSize)
9669113SPengcheng.Chen@Sun.COM block_size = DFU_MaxBlockSize;
9679113SPengcheng.Chen@Sun.COM else
9689113SPengcheng.Chen@Sun.COM block_size = bytes_left;
9699113SPengcheng.Chen@Sun.COM
9709113SPengcheng.Chen@Sun.COM err = atu_usb_request(sc, DFU_DNLOAD, block++, 0,
9719113SPengcheng.Chen@Sun.COM block_size, ptr);
9729113SPengcheng.Chen@Sun.COM if (err) {
9739113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: DFU download failed\n",
9749113SPengcheng.Chen@Sun.COM sc->sc_name);
9759113SPengcheng.Chen@Sun.COM return (err);
9769113SPengcheng.Chen@Sun.COM }
9779113SPengcheng.Chen@Sun.COM
9789113SPengcheng.Chen@Sun.COM ptr += block_size;
9799113SPengcheng.Chen@Sun.COM bytes_left -= block_size;
9809113SPengcheng.Chen@Sun.COM if (block_size == 0)
9819113SPengcheng.Chen@Sun.COM block = -1;
9829113SPengcheng.Chen@Sun.COM break;
9839113SPengcheng.Chen@Sun.COM
9849113SPengcheng.Chen@Sun.COM case DFUState_DFUError:
9859113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: DFU state error\n", sc->sc_name);
9869113SPengcheng.Chen@Sun.COM return (EIO);
9879113SPengcheng.Chen@Sun.COM
9889113SPengcheng.Chen@Sun.COM default:
9899113SPengcheng.Chen@Sun.COM drv_usecwait(10*1000);
9909113SPengcheng.Chen@Sun.COM if (++count > 100) {
9919113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: DFU timeout\n",
9929113SPengcheng.Chen@Sun.COM sc->sc_name);
9939113SPengcheng.Chen@Sun.COM return (ETIME);
9949113SPengcheng.Chen@Sun.COM }
9959113SPengcheng.Chen@Sun.COM break;
9969113SPengcheng.Chen@Sun.COM }
9979113SPengcheng.Chen@Sun.COM
9989113SPengcheng.Chen@Sun.COM state = atu_get_dfu_state(sc);
9999113SPengcheng.Chen@Sun.COM }
10009113SPengcheng.Chen@Sun.COM if (state != DFUState_ManifestSync)
10019113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: DFU state (%d) != ManifestSync\n",
10029113SPengcheng.Chen@Sun.COM sc->sc_name, state);
10039113SPengcheng.Chen@Sun.COM
10049113SPengcheng.Chen@Sun.COM err = atu_usb_request(sc, DFU_GETSTATUS, 0, 0, 6, status);
10059113SPengcheng.Chen@Sun.COM if (err) {
10069113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: DFU get status failed\n",
10079113SPengcheng.Chen@Sun.COM sc->sc_name);
10089113SPengcheng.Chen@Sun.COM return (err);
10099113SPengcheng.Chen@Sun.COM }
10109113SPengcheng.Chen@Sun.COM
10119113SPengcheng.Chen@Sun.COM err = atu_usb_request(sc, DFU_REMAP, 0, 0, 0, NULL);
10129113SPengcheng.Chen@Sun.COM if (err && !(sc->sc_quirk & ATU_QUIRK_NO_REMAP)) {
10139113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: DFU remap failed\n", sc->sc_name);
10149113SPengcheng.Chen@Sun.COM return (err);
10159113SPengcheng.Chen@Sun.COM }
10169113SPengcheng.Chen@Sun.COM
10179113SPengcheng.Chen@Sun.COM /*
10189113SPengcheng.Chen@Sun.COM * after a lot of trying and measuring I found out the device needs
10199113SPengcheng.Chen@Sun.COM * about 56 miliseconds after sending the remap command before
10209113SPengcheng.Chen@Sun.COM * it's ready to communicate again. So we'll wait just a little bit
10219113SPengcheng.Chen@Sun.COM * longer than that to be sure...
10229113SPengcheng.Chen@Sun.COM */
10239113SPengcheng.Chen@Sun.COM drv_usecwait((56+100)*1000);
10249113SPengcheng.Chen@Sun.COM
10259113SPengcheng.Chen@Sun.COM return (0);
10269113SPengcheng.Chen@Sun.COM }
10279113SPengcheng.Chen@Sun.COM
10289113SPengcheng.Chen@Sun.COM static int
atu_dfu_stage2(void * arg)10299113SPengcheng.Chen@Sun.COM atu_dfu_stage2(void *arg)
10309113SPengcheng.Chen@Sun.COM {
10319113SPengcheng.Chen@Sun.COM struct atu_softc *sc = arg;
10329113SPengcheng.Chen@Sun.COM uint8_t *ptr = NULL;
10339113SPengcheng.Chen@Sun.COM int block_size, bytes_left = 0, block = 0, err, i;
10349113SPengcheng.Chen@Sun.COM
10359113SPengcheng.Chen@Sun.COM for (i = 0; i < sizeof (atu_fw_table) / sizeof (atu_fw_table[0]); i++)
10369113SPengcheng.Chen@Sun.COM if (sc->sc_radio == atu_fw_table[i].atur_type) {
10379113SPengcheng.Chen@Sun.COM ptr = atu_fw_table[i].atur_ext;
10389113SPengcheng.Chen@Sun.COM bytes_left = atu_fw_table[i].atur_ext_size;
10399113SPengcheng.Chen@Sun.COM }
10409113SPengcheng.Chen@Sun.COM
10419113SPengcheng.Chen@Sun.COM while (bytes_left) {
10429113SPengcheng.Chen@Sun.COM if (bytes_left > 1024)
10439113SPengcheng.Chen@Sun.COM block_size = 1024;
10449113SPengcheng.Chen@Sun.COM else
10459113SPengcheng.Chen@Sun.COM block_size = bytes_left;
10469113SPengcheng.Chen@Sun.COM
10479113SPengcheng.Chen@Sun.COM err = atu_usb_request(sc, ATU_VENDOR_DEV_OUT, 0x0e,
10489113SPengcheng.Chen@Sun.COM 0x0802, block, block_size, ptr);
10499113SPengcheng.Chen@Sun.COM if (err) {
10509113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: stage2 firmware load failed\n",
10519113SPengcheng.Chen@Sun.COM sc->sc_name);
10529113SPengcheng.Chen@Sun.COM return (err);
10539113SPengcheng.Chen@Sun.COM }
10549113SPengcheng.Chen@Sun.COM
10559113SPengcheng.Chen@Sun.COM ptr += block_size;
10569113SPengcheng.Chen@Sun.COM block++;
10579113SPengcheng.Chen@Sun.COM bytes_left -= block_size;
10589113SPengcheng.Chen@Sun.COM }
10599113SPengcheng.Chen@Sun.COM
10609113SPengcheng.Chen@Sun.COM err = atu_usb_request(sc, ATU_VENDOR_DEV_OUT, 0x0e, 0x0802,
10619113SPengcheng.Chen@Sun.COM block, 0, NULL);
10629113SPengcheng.Chen@Sun.COM if (err) {
10639113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: zero-length block load failed\n",
10649113SPengcheng.Chen@Sun.COM sc->sc_name);
10659113SPengcheng.Chen@Sun.COM return (err);
10669113SPengcheng.Chen@Sun.COM }
10679113SPengcheng.Chen@Sun.COM
10689113SPengcheng.Chen@Sun.COM /*
10699113SPengcheng.Chen@Sun.COM * The SMC2662w V.4 seems to require some time to do its thing with
10709113SPengcheng.Chen@Sun.COM * the stage2 firmware... 20 ms isn't enough, but 21 ms works 100
10719113SPengcheng.Chen@Sun.COM * times out of 100 tries. We'll wait a bit longer just to be sure
10729113SPengcheng.Chen@Sun.COM */
10739113SPengcheng.Chen@Sun.COM if (sc->sc_quirk & ATU_QUIRK_FW_DELAY)
10749113SPengcheng.Chen@Sun.COM drv_usecwait((21 + 100) * 1000);
10759113SPengcheng.Chen@Sun.COM
10769113SPengcheng.Chen@Sun.COM return (0);
10779113SPengcheng.Chen@Sun.COM }
10789113SPengcheng.Chen@Sun.COM
10799113SPengcheng.Chen@Sun.COM static int
atu_load_microcode(struct atu_softc * sc,boolean_t attach)10809113SPengcheng.Chen@Sun.COM atu_load_microcode(struct atu_softc *sc, boolean_t attach)
10819113SPengcheng.Chen@Sun.COM {
10829113SPengcheng.Chen@Sun.COM usb_dev_reset_lvl_t reset;
10839113SPengcheng.Chen@Sun.COM uint8_t mode, chan;
10849113SPengcheng.Chen@Sun.COM int err;
10859113SPengcheng.Chen@Sun.COM
10869113SPengcheng.Chen@Sun.COM reset = attach ? USB_RESET_LVL_REATTACH : USB_RESET_LVL_DEFAULT;
10879113SPengcheng.Chen@Sun.COM
10889113SPengcheng.Chen@Sun.COM err = atu_get_opmode(sc, &mode);
10899113SPengcheng.Chen@Sun.COM if (!err) {
10909113SPengcheng.Chen@Sun.COM if (mode == ATU_DEV_READY)
10919113SPengcheng.Chen@Sun.COM return (0);
10929113SPengcheng.Chen@Sun.COM /*
10939113SPengcheng.Chen@Sun.COM * Opmode of SMC2662 V.4 does not change after stage2
10949113SPengcheng.Chen@Sun.COM * firmware download. If succeeded reading the channel
10959113SPengcheng.Chen@Sun.COM * number, stage2 firmware is already running.
10969113SPengcheng.Chen@Sun.COM */
10979113SPengcheng.Chen@Sun.COM if (sc->sc_radio != RadioIntersil &&
10989113SPengcheng.Chen@Sun.COM atu_get_mib(sc, MIB_PHY_CHANNEL, &chan) == 0)
10999113SPengcheng.Chen@Sun.COM return (0);
11009113SPengcheng.Chen@Sun.COM
11019113SPengcheng.Chen@Sun.COM if (mode == ATU_DEV_STAGE2)
11029113SPengcheng.Chen@Sun.COM stage2:
11039113SPengcheng.Chen@Sun.COM return (atu_dfu_stage2(sc));
11049113SPengcheng.Chen@Sun.COM }
11059113SPengcheng.Chen@Sun.COM
11069113SPengcheng.Chen@Sun.COM err = atu_dfu_stage1(sc);
11079113SPengcheng.Chen@Sun.COM if (err)
11089113SPengcheng.Chen@Sun.COM return (err);
11099113SPengcheng.Chen@Sun.COM
11109113SPengcheng.Chen@Sun.COM if (usb_reset_device(sc->sc_dip, reset) != USB_SUCCESS)
11119113SPengcheng.Chen@Sun.COM return (EIO);
11129113SPengcheng.Chen@Sun.COM
11139113SPengcheng.Chen@Sun.COM if (attach)
11149113SPengcheng.Chen@Sun.COM return (EAGAIN);
11159113SPengcheng.Chen@Sun.COM else
11169113SPengcheng.Chen@Sun.COM goto stage2;
11179113SPengcheng.Chen@Sun.COM }
11189113SPengcheng.Chen@Sun.COM
11199113SPengcheng.Chen@Sun.COM static int
atu_disconnect(dev_info_t * dip)11209113SPengcheng.Chen@Sun.COM atu_disconnect(dev_info_t *dip)
11219113SPengcheng.Chen@Sun.COM {
11229113SPengcheng.Chen@Sun.COM struct atu_softc *sc;
11239113SPengcheng.Chen@Sun.COM struct ieee80211com *ic;
11249113SPengcheng.Chen@Sun.COM
11259113SPengcheng.Chen@Sun.COM sc = ddi_get_soft_state(atu_soft_state_p, ddi_get_instance(dip));
11269113SPengcheng.Chen@Sun.COM ic = &sc->sc_ic;
11279113SPengcheng.Chen@Sun.COM
11289113SPengcheng.Chen@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
11299113SPengcheng.Chen@Sun.COM ieee80211_stop_watchdog(ic);
11309113SPengcheng.Chen@Sun.COM
11319113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
11329113SPengcheng.Chen@Sun.COM if (sc->sc_scan_timer != 0) {
11339113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
11349113SPengcheng.Chen@Sun.COM (void) untimeout(sc->sc_scan_timer);
11359113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
11369113SPengcheng.Chen@Sun.COM sc->sc_scan_timer = 0;
11379113SPengcheng.Chen@Sun.COM }
11389113SPengcheng.Chen@Sun.COM
11399113SPengcheng.Chen@Sun.COM sc->sc_flags &= ~(ATU_FLAG_RUNNING | ATU_FLAG_RADIO_ON);
11409113SPengcheng.Chen@Sun.COM atu_close_pipes(sc);
11419113SPengcheng.Chen@Sun.COM
11429113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
11439113SPengcheng.Chen@Sun.COM return (0);
11449113SPengcheng.Chen@Sun.COM }
11459113SPengcheng.Chen@Sun.COM
11469113SPengcheng.Chen@Sun.COM static int
atu_reconnect(dev_info_t * dip)11479113SPengcheng.Chen@Sun.COM atu_reconnect(dev_info_t *dip)
11489113SPengcheng.Chen@Sun.COM {
11499113SPengcheng.Chen@Sun.COM struct atu_softc *sc;
11509113SPengcheng.Chen@Sun.COM int err;
11519113SPengcheng.Chen@Sun.COM
11529113SPengcheng.Chen@Sun.COM sc = ddi_get_soft_state(atu_soft_state_p, ddi_get_instance(dip));
11539113SPengcheng.Chen@Sun.COM if (usb_check_same_device(sc->sc_dip, NULL, USB_LOG_L2, -1,
11549113SPengcheng.Chen@Sun.COM USB_CHK_BASIC, NULL) != USB_SUCCESS)
11559113SPengcheng.Chen@Sun.COM return (DDI_FAILURE);
11569113SPengcheng.Chen@Sun.COM
11579113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
11589113SPengcheng.Chen@Sun.COM err = atu_load_microcode(sc, B_FALSE);
11599113SPengcheng.Chen@Sun.COM if (!err)
11609113SPengcheng.Chen@Sun.COM err = atu_init(sc);
11619113SPengcheng.Chen@Sun.COM
11629113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
11639113SPengcheng.Chen@Sun.COM return (err ? DDI_FAILURE : DDI_SUCCESS);
11649113SPengcheng.Chen@Sun.COM }
11659113SPengcheng.Chen@Sun.COM
11669113SPengcheng.Chen@Sun.COM static int
atu_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)11679113SPengcheng.Chen@Sun.COM atu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
11689113SPengcheng.Chen@Sun.COM {
11699113SPengcheng.Chen@Sun.COM struct atu_softc *sc;
11709113SPengcheng.Chen@Sun.COM struct ieee80211com *ic;
11719113SPengcheng.Chen@Sun.COM mac_register_t *macp;
11729113SPengcheng.Chen@Sun.COM wifi_data_t wd = {0};
11739113SPengcheng.Chen@Sun.COM int instance, i, err;
11749113SPengcheng.Chen@Sun.COM
11759113SPengcheng.Chen@Sun.COM switch (cmd) {
11769113SPengcheng.Chen@Sun.COM case DDI_ATTACH:
11779113SPengcheng.Chen@Sun.COM break;
11789113SPengcheng.Chen@Sun.COM case DDI_RESUME:
11799113SPengcheng.Chen@Sun.COM sc = ddi_get_soft_state(atu_soft_state_p,
11809113SPengcheng.Chen@Sun.COM ddi_get_instance(dip));
11819113SPengcheng.Chen@Sun.COM if (usb_check_same_device(sc->sc_dip, NULL, USB_LOG_L2, -1,
11829113SPengcheng.Chen@Sun.COM USB_CHK_BASIC, NULL) != USB_SUCCESS)
11839113SPengcheng.Chen@Sun.COM return (DDI_SUCCESS);
11849113SPengcheng.Chen@Sun.COM
11859113SPengcheng.Chen@Sun.COM if (atu_load_microcode(sc, B_FALSE) == 0)
11869113SPengcheng.Chen@Sun.COM (void) atu_init(sc);
11879113SPengcheng.Chen@Sun.COM
11889113SPengcheng.Chen@Sun.COM return (DDI_SUCCESS);
11899113SPengcheng.Chen@Sun.COM default:
11909113SPengcheng.Chen@Sun.COM return (DDI_FAILURE);
11919113SPengcheng.Chen@Sun.COM }
11929113SPengcheng.Chen@Sun.COM
11939113SPengcheng.Chen@Sun.COM instance = ddi_get_instance(dip);
11949113SPengcheng.Chen@Sun.COM if (ddi_soft_state_zalloc(atu_soft_state_p, instance) != DDI_SUCCESS)
11959113SPengcheng.Chen@Sun.COM return (DDI_FAILURE);
11969113SPengcheng.Chen@Sun.COM
11979113SPengcheng.Chen@Sun.COM sc = ddi_get_soft_state(atu_soft_state_p, instance);
11989113SPengcheng.Chen@Sun.COM ic = &sc->sc_ic;
11999113SPengcheng.Chen@Sun.COM sc->sc_dip = dip;
12009113SPengcheng.Chen@Sun.COM
12019113SPengcheng.Chen@Sun.COM (void) snprintf(sc->sc_name, sizeof (sc->sc_name), "%s%d",
12029113SPengcheng.Chen@Sun.COM "atu", instance);
12039113SPengcheng.Chen@Sun.COM
12049113SPengcheng.Chen@Sun.COM err = usb_client_attach(dip, USBDRV_VERSION, 0);
12059113SPengcheng.Chen@Sun.COM if (err != USB_SUCCESS)
12069113SPengcheng.Chen@Sun.COM goto fail1;
12079113SPengcheng.Chen@Sun.COM
12089113SPengcheng.Chen@Sun.COM err = usb_get_dev_data(dip, &sc->sc_udev, USB_PARSE_LVL_ALL, 0);
12099113SPengcheng.Chen@Sun.COM if (err != USB_SUCCESS) {
12109113SPengcheng.Chen@Sun.COM sc->sc_udev = NULL;
12119113SPengcheng.Chen@Sun.COM goto fail2;
12129113SPengcheng.Chen@Sun.COM }
12139113SPengcheng.Chen@Sun.COM
12149113SPengcheng.Chen@Sun.COM for (i = 0; i < sizeof (atu_dev_table)/sizeof (atu_dev_table[0]); i++) {
12159113SPengcheng.Chen@Sun.COM struct atu_dev_type *t = &atu_dev_table[i];
12169113SPengcheng.Chen@Sun.COM if (sc->sc_udev->dev_descr->idVendor == t->atu_vid &&
12179113SPengcheng.Chen@Sun.COM sc->sc_udev->dev_descr->idProduct == t->atu_pid) {
12189113SPengcheng.Chen@Sun.COM sc->sc_radio = t->atu_radio;
12199113SPengcheng.Chen@Sun.COM sc->sc_quirk = t->atu_quirk;
12209113SPengcheng.Chen@Sun.COM }
12219113SPengcheng.Chen@Sun.COM }
12229113SPengcheng.Chen@Sun.COM
12239113SPengcheng.Chen@Sun.COM err = atu_load_microcode(sc, B_TRUE);
12249113SPengcheng.Chen@Sun.COM if (err == EAGAIN) {
12259113SPengcheng.Chen@Sun.COM sc->sc_flags |= ATU_FLAG_REATTACH; /* reattaching */
12269113SPengcheng.Chen@Sun.COM return (DDI_SUCCESS);
12279113SPengcheng.Chen@Sun.COM } else if (err) {
12289113SPengcheng.Chen@Sun.COM goto fail2;
12299113SPengcheng.Chen@Sun.COM }
12309113SPengcheng.Chen@Sun.COM sc->sc_flags &= ~ATU_FLAG_REATTACH;
12319113SPengcheng.Chen@Sun.COM
12329113SPengcheng.Chen@Sun.COM /* read device config & MAC address */
12339113SPengcheng.Chen@Sun.COM err = atu_get_config(sc);
12349113SPengcheng.Chen@Sun.COM if (err) {
12359113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: read device config failed\n",
12369113SPengcheng.Chen@Sun.COM sc->sc_name);
12379113SPengcheng.Chen@Sun.COM goto fail2;
12389113SPengcheng.Chen@Sun.COM }
12399113SPengcheng.Chen@Sun.COM
12409113SPengcheng.Chen@Sun.COM mutex_init(&sc->sc_genlock, NULL, MUTEX_DRIVER, NULL);
12419113SPengcheng.Chen@Sun.COM mutex_init(&sc->sc_txlock, NULL, MUTEX_DRIVER, NULL);
12429113SPengcheng.Chen@Sun.COM mutex_init(&sc->sc_rxlock, NULL, MUTEX_DRIVER, NULL);
12439113SPengcheng.Chen@Sun.COM
12449113SPengcheng.Chen@Sun.COM ic->ic_phytype = IEEE80211_T_DS;
12459113SPengcheng.Chen@Sun.COM ic->ic_opmode = IEEE80211_M_STA;
12469113SPengcheng.Chen@Sun.COM ic->ic_caps = IEEE80211_C_SHPREAMBLE | IEEE80211_C_WEP;
12479113SPengcheng.Chen@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11B] = atu_rateset;
12489113SPengcheng.Chen@Sun.COM ic->ic_maxrssi = atu_fw_table[sc->sc_radio].max_rssi;
12499113SPengcheng.Chen@Sun.COM ic->ic_state = IEEE80211_S_INIT;
12509113SPengcheng.Chen@Sun.COM for (i = 1; i <= 14; i++) {
12519113SPengcheng.Chen@Sun.COM ic->ic_sup_channels[i].ich_freq =
12529113SPengcheng.Chen@Sun.COM ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
12539113SPengcheng.Chen@Sun.COM ic->ic_sup_channels[i].ich_flags =
12549113SPengcheng.Chen@Sun.COM IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ |
12559113SPengcheng.Chen@Sun.COM IEEE80211_CHAN_PASSIVE;
12569113SPengcheng.Chen@Sun.COM }
12579113SPengcheng.Chen@Sun.COM ic->ic_xmit = atu_send;
12589113SPengcheng.Chen@Sun.COM ieee80211_attach(ic);
12599113SPengcheng.Chen@Sun.COM
12609113SPengcheng.Chen@Sun.COM sc->sc_newstate = ic->ic_newstate;
12619113SPengcheng.Chen@Sun.COM ic->ic_newstate = atu_newstate;
12629113SPengcheng.Chen@Sun.COM ic->ic_watchdog = atu_watchdog;
12639113SPengcheng.Chen@Sun.COM ieee80211_media_init(ic);
12649113SPengcheng.Chen@Sun.COM
12659113SPengcheng.Chen@Sun.COM ic->ic_def_txkey = 0;
12669113SPengcheng.Chen@Sun.COM wd.wd_opmode = ic->ic_opmode;
12679113SPengcheng.Chen@Sun.COM wd.wd_secalloc = WIFI_SEC_NONE;
12689113SPengcheng.Chen@Sun.COM IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
12699113SPengcheng.Chen@Sun.COM
12709113SPengcheng.Chen@Sun.COM macp = mac_alloc(MAC_VERSION);
12719113SPengcheng.Chen@Sun.COM if (macp == NULL)
12729113SPengcheng.Chen@Sun.COM goto fail3;
12739113SPengcheng.Chen@Sun.COM
12749113SPengcheng.Chen@Sun.COM macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI;
12759113SPengcheng.Chen@Sun.COM macp->m_driver = sc;
12769113SPengcheng.Chen@Sun.COM macp->m_dip = dip;
12779113SPengcheng.Chen@Sun.COM macp->m_src_addr = ic->ic_macaddr;
12789113SPengcheng.Chen@Sun.COM macp->m_callbacks = &atu_m_callbacks;
12799113SPengcheng.Chen@Sun.COM macp->m_min_sdu = 0;
12809113SPengcheng.Chen@Sun.COM macp->m_max_sdu = IEEE80211_MTU;
12819113SPengcheng.Chen@Sun.COM macp->m_pdata = &wd;
12829113SPengcheng.Chen@Sun.COM macp->m_pdata_size = sizeof (wd);
12839113SPengcheng.Chen@Sun.COM
12849113SPengcheng.Chen@Sun.COM err = mac_register(macp, &ic->ic_mach);
12859113SPengcheng.Chen@Sun.COM mac_free(macp);
12869113SPengcheng.Chen@Sun.COM if (err)
12879113SPengcheng.Chen@Sun.COM goto fail3;
12889113SPengcheng.Chen@Sun.COM
12899113SPengcheng.Chen@Sun.COM err = usb_register_hotplug_cbs(sc->sc_dip, atu_disconnect,
12909113SPengcheng.Chen@Sun.COM atu_reconnect);
12919113SPengcheng.Chen@Sun.COM if (err != USB_SUCCESS)
12929113SPengcheng.Chen@Sun.COM goto fail4;
12939113SPengcheng.Chen@Sun.COM
12949113SPengcheng.Chen@Sun.COM err = ddi_create_minor_node(dip, sc->sc_name, S_IFCHR,
12959113SPengcheng.Chen@Sun.COM instance + 1, DDI_NT_NET_WIFI, 0);
12969113SPengcheng.Chen@Sun.COM if (err != DDI_SUCCESS)
12979113SPengcheng.Chen@Sun.COM cmn_err(CE_WARN, "%s: minor node creation failed\n",
12989113SPengcheng.Chen@Sun.COM sc->sc_name);
12999113SPengcheng.Chen@Sun.COM
13009113SPengcheng.Chen@Sun.COM mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
13019113SPengcheng.Chen@Sun.COM return (DDI_SUCCESS);
13029113SPengcheng.Chen@Sun.COM
13039113SPengcheng.Chen@Sun.COM fail4:
13049113SPengcheng.Chen@Sun.COM (void) mac_unregister(ic->ic_mach);
13059113SPengcheng.Chen@Sun.COM fail3:
13069113SPengcheng.Chen@Sun.COM mutex_destroy(&sc->sc_genlock);
13079113SPengcheng.Chen@Sun.COM mutex_destroy(&sc->sc_rxlock);
13089113SPengcheng.Chen@Sun.COM mutex_destroy(&sc->sc_txlock);
13099113SPengcheng.Chen@Sun.COM fail2:
13109113SPengcheng.Chen@Sun.COM usb_client_detach(sc->sc_dip, sc->sc_udev);
13119113SPengcheng.Chen@Sun.COM fail1:
13129113SPengcheng.Chen@Sun.COM ddi_soft_state_free(atu_soft_state_p, instance);
13139113SPengcheng.Chen@Sun.COM
13149113SPengcheng.Chen@Sun.COM return (DDI_FAILURE);
13159113SPengcheng.Chen@Sun.COM }
13169113SPengcheng.Chen@Sun.COM
13179113SPengcheng.Chen@Sun.COM static int
atu_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)13189113SPengcheng.Chen@Sun.COM atu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
13199113SPengcheng.Chen@Sun.COM {
13209113SPengcheng.Chen@Sun.COM struct atu_softc *sc;
13219113SPengcheng.Chen@Sun.COM int err;
13229113SPengcheng.Chen@Sun.COM
13239113SPengcheng.Chen@Sun.COM sc = ddi_get_soft_state(atu_soft_state_p, ddi_get_instance(dip));
13249113SPengcheng.Chen@Sun.COM
13259113SPengcheng.Chen@Sun.COM switch (cmd) {
13269113SPengcheng.Chen@Sun.COM case DDI_DETACH:
13279113SPengcheng.Chen@Sun.COM break;
13289113SPengcheng.Chen@Sun.COM case DDI_SUSPEND:
13299113SPengcheng.Chen@Sun.COM ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
13309113SPengcheng.Chen@Sun.COM ieee80211_stop_watchdog(&sc->sc_ic);
13319113SPengcheng.Chen@Sun.COM
13329113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
13339113SPengcheng.Chen@Sun.COM if (sc->sc_scan_timer != 0) {
13349113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
13359113SPengcheng.Chen@Sun.COM (void) untimeout(sc->sc_scan_timer);
13369113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
13379113SPengcheng.Chen@Sun.COM sc->sc_scan_timer = 0;
13389113SPengcheng.Chen@Sun.COM }
13399113SPengcheng.Chen@Sun.COM (void) atu_stop(sc);
13409113SPengcheng.Chen@Sun.COM
13419113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
13429113SPengcheng.Chen@Sun.COM return (DDI_SUCCESS);
13439113SPengcheng.Chen@Sun.COM default:
13449113SPengcheng.Chen@Sun.COM return (DDI_FAILURE);
13459113SPengcheng.Chen@Sun.COM }
13469113SPengcheng.Chen@Sun.COM
13479113SPengcheng.Chen@Sun.COM if (!ATU_REATTACH(sc)) {
13489113SPengcheng.Chen@Sun.COM err = mac_disable(sc->sc_ic.ic_mach);
13499113SPengcheng.Chen@Sun.COM if (err)
13509113SPengcheng.Chen@Sun.COM return (DDI_FAILURE);
13519113SPengcheng.Chen@Sun.COM
13529113SPengcheng.Chen@Sun.COM (void) atu_stop(sc);
13539113SPengcheng.Chen@Sun.COM
13549113SPengcheng.Chen@Sun.COM usb_unregister_hotplug_cbs(dip);
13559113SPengcheng.Chen@Sun.COM (void) mac_unregister(sc->sc_ic.ic_mach);
13569113SPengcheng.Chen@Sun.COM ieee80211_detach(&sc->sc_ic);
13579113SPengcheng.Chen@Sun.COM
13589113SPengcheng.Chen@Sun.COM mutex_destroy(&sc->sc_genlock);
13599113SPengcheng.Chen@Sun.COM mutex_destroy(&sc->sc_txlock);
13609113SPengcheng.Chen@Sun.COM mutex_destroy(&sc->sc_rxlock);
13619113SPengcheng.Chen@Sun.COM
13629113SPengcheng.Chen@Sun.COM ddi_remove_minor_node(dip, NULL);
13639113SPengcheng.Chen@Sun.COM }
13649113SPengcheng.Chen@Sun.COM
13659113SPengcheng.Chen@Sun.COM usb_client_detach(dip, sc->sc_udev);
13669113SPengcheng.Chen@Sun.COM ddi_soft_state_free(atu_soft_state_p, ddi_get_instance(dip));
13679113SPengcheng.Chen@Sun.COM
13689113SPengcheng.Chen@Sun.COM return (DDI_SUCCESS);
13699113SPengcheng.Chen@Sun.COM }
13709113SPengcheng.Chen@Sun.COM
13719113SPengcheng.Chen@Sun.COM DDI_DEFINE_STREAM_OPS(atu_dev_ops, nulldev, nulldev, atu_attach,
13729113SPengcheng.Chen@Sun.COM atu_detach, nodev, NULL, D_MP, NULL, ddi_quiesce_not_needed);
13739113SPengcheng.Chen@Sun.COM
13749113SPengcheng.Chen@Sun.COM static struct modldrv atu_modldrv = {
13759113SPengcheng.Chen@Sun.COM &mod_driverops,
13769113SPengcheng.Chen@Sun.COM "atu driver v1.1",
13779113SPengcheng.Chen@Sun.COM &atu_dev_ops
13789113SPengcheng.Chen@Sun.COM };
13799113SPengcheng.Chen@Sun.COM
13809113SPengcheng.Chen@Sun.COM static struct modlinkage modlinkage = {
13819113SPengcheng.Chen@Sun.COM MODREV_1,
13829113SPengcheng.Chen@Sun.COM (void *)&atu_modldrv,
13839113SPengcheng.Chen@Sun.COM NULL
13849113SPengcheng.Chen@Sun.COM };
13859113SPengcheng.Chen@Sun.COM
13869113SPengcheng.Chen@Sun.COM int
_info(struct modinfo * modinfop)13879113SPengcheng.Chen@Sun.COM _info(struct modinfo *modinfop)
13889113SPengcheng.Chen@Sun.COM {
13899113SPengcheng.Chen@Sun.COM return (mod_info(&modlinkage, modinfop));
13909113SPengcheng.Chen@Sun.COM }
13919113SPengcheng.Chen@Sun.COM
13929113SPengcheng.Chen@Sun.COM int
_init(void)13939113SPengcheng.Chen@Sun.COM _init(void)
13949113SPengcheng.Chen@Sun.COM {
13959113SPengcheng.Chen@Sun.COM int status;
13969113SPengcheng.Chen@Sun.COM
13979113SPengcheng.Chen@Sun.COM status = ddi_soft_state_init(&atu_soft_state_p,
13989113SPengcheng.Chen@Sun.COM sizeof (struct atu_softc), 1);
13999113SPengcheng.Chen@Sun.COM if (status != 0)
14009113SPengcheng.Chen@Sun.COM return (status);
14019113SPengcheng.Chen@Sun.COM
14029113SPengcheng.Chen@Sun.COM mac_init_ops(&atu_dev_ops, "atu");
14039113SPengcheng.Chen@Sun.COM status = mod_install(&modlinkage);
14049113SPengcheng.Chen@Sun.COM if (status != 0) {
14059113SPengcheng.Chen@Sun.COM mac_fini_ops(&atu_dev_ops);
14069113SPengcheng.Chen@Sun.COM ddi_soft_state_fini(&atu_soft_state_p);
14079113SPengcheng.Chen@Sun.COM }
14089113SPengcheng.Chen@Sun.COM return (status);
14099113SPengcheng.Chen@Sun.COM }
14109113SPengcheng.Chen@Sun.COM
14119113SPengcheng.Chen@Sun.COM int
_fini(void)14129113SPengcheng.Chen@Sun.COM _fini(void)
14139113SPengcheng.Chen@Sun.COM {
14149113SPengcheng.Chen@Sun.COM int status;
14159113SPengcheng.Chen@Sun.COM
14169113SPengcheng.Chen@Sun.COM status = mod_remove(&modlinkage);
14179113SPengcheng.Chen@Sun.COM if (status == 0) {
14189113SPengcheng.Chen@Sun.COM mac_fini_ops(&atu_dev_ops);
14199113SPengcheng.Chen@Sun.COM ddi_soft_state_fini(&atu_soft_state_p);
14209113SPengcheng.Chen@Sun.COM }
14219113SPengcheng.Chen@Sun.COM return (status);
14229113SPengcheng.Chen@Sun.COM }
14239113SPengcheng.Chen@Sun.COM
14249113SPengcheng.Chen@Sun.COM static int
atu_m_start(void * arg)14259113SPengcheng.Chen@Sun.COM atu_m_start(void *arg)
14269113SPengcheng.Chen@Sun.COM {
14279113SPengcheng.Chen@Sun.COM struct atu_softc *sc = (struct atu_softc *)arg;
14289113SPengcheng.Chen@Sun.COM int err;
14299113SPengcheng.Chen@Sun.COM
14309113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
14319113SPengcheng.Chen@Sun.COM err = atu_init(sc);
14329113SPengcheng.Chen@Sun.COM
14339113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
14349113SPengcheng.Chen@Sun.COM return (err);
14359113SPengcheng.Chen@Sun.COM }
14369113SPengcheng.Chen@Sun.COM
14379113SPengcheng.Chen@Sun.COM static void
atu_m_stop(void * arg)14389113SPengcheng.Chen@Sun.COM atu_m_stop(void *arg)
14399113SPengcheng.Chen@Sun.COM {
14409113SPengcheng.Chen@Sun.COM struct atu_softc *sc = (struct atu_softc *)arg;
14419113SPengcheng.Chen@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
14429113SPengcheng.Chen@Sun.COM
14439113SPengcheng.Chen@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
14449113SPengcheng.Chen@Sun.COM ieee80211_stop_watchdog(ic);
14459113SPengcheng.Chen@Sun.COM
14469113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
14479113SPengcheng.Chen@Sun.COM if (sc->sc_scan_timer != 0) {
14489113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
14499113SPengcheng.Chen@Sun.COM (void) untimeout(sc->sc_scan_timer);
14509113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
14519113SPengcheng.Chen@Sun.COM sc->sc_scan_timer = 0;
14529113SPengcheng.Chen@Sun.COM }
14539113SPengcheng.Chen@Sun.COM (void) atu_stop(sc);
14549113SPengcheng.Chen@Sun.COM
14559113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
14569113SPengcheng.Chen@Sun.COM }
14579113SPengcheng.Chen@Sun.COM
14589113SPengcheng.Chen@Sun.COM /*ARGSUSED*/
14599113SPengcheng.Chen@Sun.COM static int
atu_m_unicst(void * arg,const uint8_t * macaddr)14609113SPengcheng.Chen@Sun.COM atu_m_unicst(void *arg, const uint8_t *macaddr)
14619113SPengcheng.Chen@Sun.COM {
14629113SPengcheng.Chen@Sun.COM return (ENOTSUP);
14639113SPengcheng.Chen@Sun.COM }
14649113SPengcheng.Chen@Sun.COM
14659113SPengcheng.Chen@Sun.COM /*ARGSUSED*/
14669113SPengcheng.Chen@Sun.COM static int
atu_m_multicst(void * arg,boolean_t add,const uint8_t * mca)14679113SPengcheng.Chen@Sun.COM atu_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
14689113SPengcheng.Chen@Sun.COM {
14699113SPengcheng.Chen@Sun.COM return (ENOTSUP);
14709113SPengcheng.Chen@Sun.COM }
14719113SPengcheng.Chen@Sun.COM
14729113SPengcheng.Chen@Sun.COM /*ARGSUSED*/
14739113SPengcheng.Chen@Sun.COM static int
atu_m_promisc(void * arg,boolean_t on)14749113SPengcheng.Chen@Sun.COM atu_m_promisc(void *arg, boolean_t on)
14759113SPengcheng.Chen@Sun.COM {
14769113SPengcheng.Chen@Sun.COM return (0);
14779113SPengcheng.Chen@Sun.COM }
14789113SPengcheng.Chen@Sun.COM
14799113SPengcheng.Chen@Sun.COM static int
atu_m_setprop(void * arg,const char * name,mac_prop_id_t id,uint_t len,const void * buf)14809113SPengcheng.Chen@Sun.COM atu_m_setprop(void *arg, const char *name, mac_prop_id_t id, uint_t len,
14819113SPengcheng.Chen@Sun.COM const void *buf)
14829113SPengcheng.Chen@Sun.COM {
14839113SPengcheng.Chen@Sun.COM struct atu_softc *sc = (struct atu_softc *)arg;
14849113SPengcheng.Chen@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
14859113SPengcheng.Chen@Sun.COM int err;
14869113SPengcheng.Chen@Sun.COM
14879113SPengcheng.Chen@Sun.COM err = ieee80211_setprop(ic, name, id, len, buf);
14889113SPengcheng.Chen@Sun.COM if (err != ENETRESET)
14899113SPengcheng.Chen@Sun.COM return (err);
14909113SPengcheng.Chen@Sun.COM if (ic->ic_des_esslen == 0)
14919113SPengcheng.Chen@Sun.COM return (0);
14929113SPengcheng.Chen@Sun.COM
14939113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
14949113SPengcheng.Chen@Sun.COM if (ATU_RUNNING(sc)) {
14959113SPengcheng.Chen@Sun.COM if (sc->sc_scan_timer != 0) {
14969113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
14979113SPengcheng.Chen@Sun.COM (void) untimeout(sc->sc_scan_timer);
14989113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
14999113SPengcheng.Chen@Sun.COM sc->sc_scan_timer = 0;
15009113SPengcheng.Chen@Sun.COM }
15019113SPengcheng.Chen@Sun.COM err = atu_init(sc);
15029113SPengcheng.Chen@Sun.COM
15039113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
15049113SPengcheng.Chen@Sun.COM if (err)
15059113SPengcheng.Chen@Sun.COM return (err);
15069113SPengcheng.Chen@Sun.COM ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
15079113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
15089113SPengcheng.Chen@Sun.COM }
15099113SPengcheng.Chen@Sun.COM
15109113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
15119113SPengcheng.Chen@Sun.COM return (0);
15129113SPengcheng.Chen@Sun.COM }
15139113SPengcheng.Chen@Sun.COM
1514*11878SVenu.Iyer@Sun.COM static int
atu_m_getprop(void * arg,const char * name,mac_prop_id_t id,uint_t length,void * buf)1515*11878SVenu.Iyer@Sun.COM atu_m_getprop(void *arg, const char *name, mac_prop_id_t id,
1516*11878SVenu.Iyer@Sun.COM uint_t length, void *buf)
1517*11878SVenu.Iyer@Sun.COM {
1518*11878SVenu.Iyer@Sun.COM struct atu_softc *sc = (struct atu_softc *)arg;
1519*11878SVenu.Iyer@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
1520*11878SVenu.Iyer@Sun.COM
1521*11878SVenu.Iyer@Sun.COM return (ieee80211_getprop(ic, name, id, length, buf));
1522*11878SVenu.Iyer@Sun.COM }
1523*11878SVenu.Iyer@Sun.COM
1524*11878SVenu.Iyer@Sun.COM static void
atu_m_propinfo(void * arg,const char * name,mac_prop_id_t id,mac_prop_info_handle_t mph)1525*11878SVenu.Iyer@Sun.COM atu_m_propinfo(void *arg, const char *name, mac_prop_id_t id,
1526*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t mph)
1527*11878SVenu.Iyer@Sun.COM {
1528*11878SVenu.Iyer@Sun.COM struct atu_softc *sc = (struct atu_softc *)arg;
1529*11878SVenu.Iyer@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
1530*11878SVenu.Iyer@Sun.COM
1531*11878SVenu.Iyer@Sun.COM ieee80211_propinfo(ic, name, id, mph);
1532*11878SVenu.Iyer@Sun.COM }
1533*11878SVenu.Iyer@Sun.COM
15349113SPengcheng.Chen@Sun.COM static void
atu_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)15359113SPengcheng.Chen@Sun.COM atu_m_ioctl(void* arg, queue_t *wq, mblk_t *mp)
15369113SPengcheng.Chen@Sun.COM {
15379113SPengcheng.Chen@Sun.COM struct atu_softc *sc = (struct atu_softc *)arg;
15389113SPengcheng.Chen@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
15399113SPengcheng.Chen@Sun.COM int err;
15409113SPengcheng.Chen@Sun.COM
15419113SPengcheng.Chen@Sun.COM err = ieee80211_ioctl(ic, wq, mp);
15429113SPengcheng.Chen@Sun.COM if (err != ENETRESET || ic->ic_des_esslen == 0)
15439113SPengcheng.Chen@Sun.COM return;
15449113SPengcheng.Chen@Sun.COM
15459113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
15469113SPengcheng.Chen@Sun.COM if (ATU_RUNNING(sc)) {
15479113SPengcheng.Chen@Sun.COM if (sc->sc_scan_timer != 0) {
15489113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
15499113SPengcheng.Chen@Sun.COM (void) untimeout(sc->sc_scan_timer);
15509113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
15519113SPengcheng.Chen@Sun.COM sc->sc_scan_timer = 0;
15529113SPengcheng.Chen@Sun.COM }
15539113SPengcheng.Chen@Sun.COM err = atu_init(sc);
15549113SPengcheng.Chen@Sun.COM
15559113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
15569113SPengcheng.Chen@Sun.COM if (err)
15579113SPengcheng.Chen@Sun.COM return;
15589113SPengcheng.Chen@Sun.COM ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
15599113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
15609113SPengcheng.Chen@Sun.COM }
15619113SPengcheng.Chen@Sun.COM
15629113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
15639113SPengcheng.Chen@Sun.COM }
15649113SPengcheng.Chen@Sun.COM
15659113SPengcheng.Chen@Sun.COM static mblk_t *
atu_m_tx(void * arg,mblk_t * mp)15669113SPengcheng.Chen@Sun.COM atu_m_tx(void *arg, mblk_t *mp)
15679113SPengcheng.Chen@Sun.COM {
15689113SPengcheng.Chen@Sun.COM struct atu_softc *sc = (struct atu_softc *)arg;
15699113SPengcheng.Chen@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
15709113SPengcheng.Chen@Sun.COM mblk_t *next;
15719113SPengcheng.Chen@Sun.COM
15729113SPengcheng.Chen@Sun.COM if (ic->ic_state != IEEE80211_S_RUN) {
15739113SPengcheng.Chen@Sun.COM freemsgchain(mp);
15749113SPengcheng.Chen@Sun.COM return (NULL);
15759113SPengcheng.Chen@Sun.COM }
15769113SPengcheng.Chen@Sun.COM
15779113SPengcheng.Chen@Sun.COM while (mp != NULL) {
15789113SPengcheng.Chen@Sun.COM next = mp->b_next;
15799113SPengcheng.Chen@Sun.COM mp->b_next = NULL;
15809113SPengcheng.Chen@Sun.COM if (atu_send(ic, mp, IEEE80211_FC0_TYPE_DATA) == ENOMEM) {
15819113SPengcheng.Chen@Sun.COM mutex_enter(&sc->sc_txlock);
15829113SPengcheng.Chen@Sun.COM sc->sc_need_sched = 1;
15839113SPengcheng.Chen@Sun.COM mutex_exit(&sc->sc_txlock);
15849113SPengcheng.Chen@Sun.COM
15859113SPengcheng.Chen@Sun.COM mp->b_next = next;
15869113SPengcheng.Chen@Sun.COM return (mp);
15879113SPengcheng.Chen@Sun.COM }
15889113SPengcheng.Chen@Sun.COM mp = next;
15899113SPengcheng.Chen@Sun.COM }
15909113SPengcheng.Chen@Sun.COM
15919113SPengcheng.Chen@Sun.COM return (mp);
15929113SPengcheng.Chen@Sun.COM }
15939113SPengcheng.Chen@Sun.COM
15949113SPengcheng.Chen@Sun.COM static int
atu_m_stat(void * arg,uint_t stat,uint64_t * val)15959113SPengcheng.Chen@Sun.COM atu_m_stat(void *arg, uint_t stat, uint64_t *val)
15969113SPengcheng.Chen@Sun.COM {
15979113SPengcheng.Chen@Sun.COM struct atu_softc *sc = (struct atu_softc *)arg;
15989113SPengcheng.Chen@Sun.COM ieee80211com_t *ic = &sc->sc_ic;
15999113SPengcheng.Chen@Sun.COM ieee80211_node_t *in;
16009113SPengcheng.Chen@Sun.COM
16019113SPengcheng.Chen@Sun.COM ATU_LOCK(sc);
16029113SPengcheng.Chen@Sun.COM switch (stat) {
16039113SPengcheng.Chen@Sun.COM case MAC_STAT_IFSPEED:
16049113SPengcheng.Chen@Sun.COM in = ic->ic_bss;
16059113SPengcheng.Chen@Sun.COM *val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
16069113SPengcheng.Chen@Sun.COM IEEE80211_RATE(in->in_txrate) :
16079113SPengcheng.Chen@Sun.COM ic->ic_fixed_rate) / 2 * 1000000;
16089113SPengcheng.Chen@Sun.COM break;
16099113SPengcheng.Chen@Sun.COM case MAC_STAT_NOXMTBUF:
16109113SPengcheng.Chen@Sun.COM *val = sc->sc_tx_nobuf;
16119113SPengcheng.Chen@Sun.COM break;
16129113SPengcheng.Chen@Sun.COM case MAC_STAT_NORCVBUF:
16139113SPengcheng.Chen@Sun.COM *val = sc->sc_rx_nobuf;
16149113SPengcheng.Chen@Sun.COM break;
16159113SPengcheng.Chen@Sun.COM case MAC_STAT_IERRORS:
16169113SPengcheng.Chen@Sun.COM *val = sc->sc_rx_err;
16179113SPengcheng.Chen@Sun.COM break;
16189113SPengcheng.Chen@Sun.COM case MAC_STAT_RBYTES:
16199113SPengcheng.Chen@Sun.COM *val = ic->ic_stats.is_rx_bytes;
16209113SPengcheng.Chen@Sun.COM break;
16219113SPengcheng.Chen@Sun.COM case MAC_STAT_IPACKETS:
16229113SPengcheng.Chen@Sun.COM *val = ic->ic_stats.is_rx_frags;
16239113SPengcheng.Chen@Sun.COM break;
16249113SPengcheng.Chen@Sun.COM case MAC_STAT_OBYTES:
16259113SPengcheng.Chen@Sun.COM *val = ic->ic_stats.is_tx_bytes;
16269113SPengcheng.Chen@Sun.COM break;
16279113SPengcheng.Chen@Sun.COM case MAC_STAT_OPACKETS:
16289113SPengcheng.Chen@Sun.COM *val = ic->ic_stats.is_tx_frags;
16299113SPengcheng.Chen@Sun.COM break;
16309113SPengcheng.Chen@Sun.COM case MAC_STAT_OERRORS:
16319113SPengcheng.Chen@Sun.COM *val = ic->ic_stats.is_tx_failed;
16329113SPengcheng.Chen@Sun.COM break;
16339113SPengcheng.Chen@Sun.COM case WIFI_STAT_TX_FRAGS:
16349113SPengcheng.Chen@Sun.COM case WIFI_STAT_MCAST_TX:
16359113SPengcheng.Chen@Sun.COM case WIFI_STAT_TX_FAILED:
16369113SPengcheng.Chen@Sun.COM case WIFI_STAT_TX_RETRANS:
16379113SPengcheng.Chen@Sun.COM case WIFI_STAT_TX_RERETRANS:
16389113SPengcheng.Chen@Sun.COM case WIFI_STAT_RTS_SUCCESS:
16399113SPengcheng.Chen@Sun.COM case WIFI_STAT_RTS_FAILURE:
16409113SPengcheng.Chen@Sun.COM case WIFI_STAT_ACK_FAILURE:
16419113SPengcheng.Chen@Sun.COM case WIFI_STAT_RX_FRAGS:
16429113SPengcheng.Chen@Sun.COM case WIFI_STAT_MCAST_RX:
16439113SPengcheng.Chen@Sun.COM case WIFI_STAT_FCS_ERRORS:
16449113SPengcheng.Chen@Sun.COM case WIFI_STAT_WEP_ERRORS:
16459113SPengcheng.Chen@Sun.COM case WIFI_STAT_RX_DUPS:
16469113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
16479113SPengcheng.Chen@Sun.COM return (ieee80211_stat(ic, stat, val));
16489113SPengcheng.Chen@Sun.COM default:
16499113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
16509113SPengcheng.Chen@Sun.COM return (ENOTSUP);
16519113SPengcheng.Chen@Sun.COM }
16529113SPengcheng.Chen@Sun.COM
16539113SPengcheng.Chen@Sun.COM ATU_UNLOCK(sc);
16549113SPengcheng.Chen@Sun.COM return (0);
16559113SPengcheng.Chen@Sun.COM }
16569113SPengcheng.Chen@Sun.COM
16579113SPengcheng.Chen@Sun.COM static mac_callbacks_t atu_m_callbacks = {
1658*11878SVenu.Iyer@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
16599113SPengcheng.Chen@Sun.COM atu_m_stat,
16609113SPengcheng.Chen@Sun.COM atu_m_start,
16619113SPengcheng.Chen@Sun.COM atu_m_stop,
16629113SPengcheng.Chen@Sun.COM atu_m_promisc,
16639113SPengcheng.Chen@Sun.COM atu_m_multicst,
16649113SPengcheng.Chen@Sun.COM atu_m_unicst,
16659113SPengcheng.Chen@Sun.COM atu_m_tx,
1666*11878SVenu.Iyer@Sun.COM NULL,
16679113SPengcheng.Chen@Sun.COM atu_m_ioctl,
16689113SPengcheng.Chen@Sun.COM NULL,
16699113SPengcheng.Chen@Sun.COM NULL,
16709113SPengcheng.Chen@Sun.COM NULL,
16719113SPengcheng.Chen@Sun.COM atu_m_setprop,
1672*11878SVenu.Iyer@Sun.COM atu_m_getprop,
1673*11878SVenu.Iyer@Sun.COM atu_m_propinfo
16749113SPengcheng.Chen@Sun.COM };
1675