110128SFei.Feng@Sun.COM /*
2*11878SVenu.Iyer@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
310128SFei.Feng@Sun.COM * Use is subject to license terms.
410128SFei.Feng@Sun.COM */
510128SFei.Feng@Sun.COM
610128SFei.Feng@Sun.COM /*
710128SFei.Feng@Sun.COM * Copyright (c) 2006 Sam Leffler, Errno Consulting
810128SFei.Feng@Sun.COM * Copyright (c) 2008-2009 Weongyo Jeong <weongyo@freebsd.org>
910128SFei.Feng@Sun.COM * All rights reserved.
1010128SFei.Feng@Sun.COM *
1110128SFei.Feng@Sun.COM * Redistribution and use in source and binary forms, with or without
1210128SFei.Feng@Sun.COM * modification, are permitted provided that the following conditions
1310128SFei.Feng@Sun.COM * are met:
1410128SFei.Feng@Sun.COM * 1. Redistributions of source code must retain the above copyright
1510128SFei.Feng@Sun.COM * notice, this list of conditions and the following disclaimer,
1610128SFei.Feng@Sun.COM * without modification.
1710128SFei.Feng@Sun.COM * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1810128SFei.Feng@Sun.COM * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
1910128SFei.Feng@Sun.COM * redistribution must be conditioned upon including a substantially
2010128SFei.Feng@Sun.COM * similar Disclaimer requirement for further binary redistribution.
2110128SFei.Feng@Sun.COM *
2210128SFei.Feng@Sun.COM * NO WARRANTY
2310128SFei.Feng@Sun.COM * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2410128SFei.Feng@Sun.COM * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2510128SFei.Feng@Sun.COM * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
2610128SFei.Feng@Sun.COM * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
2710128SFei.Feng@Sun.COM * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
2810128SFei.Feng@Sun.COM * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2910128SFei.Feng@Sun.COM * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3010128SFei.Feng@Sun.COM * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
3110128SFei.Feng@Sun.COM * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3210128SFei.Feng@Sun.COM * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3310128SFei.Feng@Sun.COM * THE POSSIBILITY OF SUCH DAMAGES.
3410128SFei.Feng@Sun.COM */
3510128SFei.Feng@Sun.COM
3610128SFei.Feng@Sun.COM /*
3710128SFei.Feng@Sun.COM * This driver is distantly derived from a driver of the same name
3810128SFei.Feng@Sun.COM * by Damien Bergamini. The original copyright is included below:
3910128SFei.Feng@Sun.COM *
4010128SFei.Feng@Sun.COM * Copyright (c) 2006
4110128SFei.Feng@Sun.COM * Damien Bergamini <damien.bergamini@free.fr>
4210128SFei.Feng@Sun.COM *
4310128SFei.Feng@Sun.COM * Permission to use, copy, modify, and distribute this software for any
4410128SFei.Feng@Sun.COM * purpose with or without fee is hereby granted, provided that the above
4510128SFei.Feng@Sun.COM * copyright notice and this permission notice appear in all copies.
4610128SFei.Feng@Sun.COM *
4710128SFei.Feng@Sun.COM * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
4810128SFei.Feng@Sun.COM * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
4910128SFei.Feng@Sun.COM * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
5010128SFei.Feng@Sun.COM * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
5110128SFei.Feng@Sun.COM * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
5210128SFei.Feng@Sun.COM * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
5310128SFei.Feng@Sun.COM * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
5410128SFei.Feng@Sun.COM */
5510128SFei.Feng@Sun.COM
5610128SFei.Feng@Sun.COM
5710128SFei.Feng@Sun.COM #include <sys/types.h>
5810128SFei.Feng@Sun.COM #include <sys/cmn_err.h>
5910128SFei.Feng@Sun.COM #include <sys/strsubr.h>
6010128SFei.Feng@Sun.COM #include <sys/strsun.h>
6110128SFei.Feng@Sun.COM #include <sys/modctl.h>
6210128SFei.Feng@Sun.COM #include <sys/devops.h>
63*11878SVenu.Iyer@Sun.COM #include <sys/byteorder.h>
6410128SFei.Feng@Sun.COM #include <sys/mac_provider.h>
6510128SFei.Feng@Sun.COM #include <sys/mac_wifi.h>
6610128SFei.Feng@Sun.COM #include <sys/net80211.h>
6710128SFei.Feng@Sun.COM
6810128SFei.Feng@Sun.COM #define USBDRV_MAJOR_VER 2
6910128SFei.Feng@Sun.COM #define USBDRV_MINOR_VER 0
7010128SFei.Feng@Sun.COM #include <sys/usb/usba.h>
7110128SFei.Feng@Sun.COM #include <sys/usb/usba/usba_types.h>
7210128SFei.Feng@Sun.COM
7310128SFei.Feng@Sun.COM #include "uath_reg.h"
7410128SFei.Feng@Sun.COM #include "uath_var.h"
7510128SFei.Feng@Sun.COM
7610128SFei.Feng@Sun.COM static void *uath_soft_state_p = NULL;
7710128SFei.Feng@Sun.COM
7810128SFei.Feng@Sun.COM /*
7910128SFei.Feng@Sun.COM * Bit flags in the ral_dbg_flags
8010128SFei.Feng@Sun.COM */
8110128SFei.Feng@Sun.COM #define UATH_DBG_MSG 0x000001
8210128SFei.Feng@Sun.COM #define UATH_DBG_ERR 0x000002
8310128SFei.Feng@Sun.COM #define UATH_DBG_USB 0x000004
8410128SFei.Feng@Sun.COM #define UATH_DBG_TX 0x000008
8510128SFei.Feng@Sun.COM #define UATH_DBG_RX 0x000010
8610128SFei.Feng@Sun.COM #define UATH_DBG_FW 0x000020
8710128SFei.Feng@Sun.COM #define UATH_DBG_TX_CMD 0x000040
8810128SFei.Feng@Sun.COM #define UATH_DBG_RX_CMD 0x000080
8910128SFei.Feng@Sun.COM #define UATH_DBG_ALL 0x000fff
9010128SFei.Feng@Sun.COM
9110128SFei.Feng@Sun.COM uint32_t uath_dbg_flags = 0;
9210128SFei.Feng@Sun.COM
9310128SFei.Feng@Sun.COM #ifdef DEBUG
9410128SFei.Feng@Sun.COM #define UATH_DEBUG \
9510128SFei.Feng@Sun.COM uath_debug
9610128SFei.Feng@Sun.COM #else
9710128SFei.Feng@Sun.COM #define UATH_DEBUG
9810128SFei.Feng@Sun.COM #endif
9910128SFei.Feng@Sun.COM
10010128SFei.Feng@Sun.COM /*
10110128SFei.Feng@Sun.COM * Various supported device vendors/products.
10210128SFei.Feng@Sun.COM * UB51: AR5005UG 802.11b/g, UB52: AR5005UX 802.11b/g
10310128SFei.Feng@Sun.COM */
10410128SFei.Feng@Sun.COM #define UATH_FLAG_PRE_FIRMWARE (1 << 0)
10510128SFei.Feng@Sun.COM #define UATH_FLAG_ABG (1 << 1)
10610128SFei.Feng@Sun.COM #define UATH_FLAG_ERR (1 << 2)
10710128SFei.Feng@Sun.COM #define UATH_DEV(v, p, f) \
10810128SFei.Feng@Sun.COM { { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }, (f) }, \
10910128SFei.Feng@Sun.COM { { USB_VENDOR_##v, USB_PRODUCT_##v##_##p##_NF }, \
11010128SFei.Feng@Sun.COM (f) | UATH_FLAG_PRE_FIRMWARE }
11110128SFei.Feng@Sun.COM #define UATH_DEV_UG(v, p) UATH_DEV(v, p, 0)
11210128SFei.Feng@Sun.COM #define UATH_DEV_UX(v, p) UATH_DEV(v, p, UATH_FLAG_ABG)
11310128SFei.Feng@Sun.COM
11410128SFei.Feng@Sun.COM struct uath_devno {
11510128SFei.Feng@Sun.COM uint16_t vendor_id;
11610128SFei.Feng@Sun.COM uint16_t product_id;
11710128SFei.Feng@Sun.COM };
11810128SFei.Feng@Sun.COM
11910128SFei.Feng@Sun.COM static const struct uath_type {
12010128SFei.Feng@Sun.COM struct uath_devno dev;
12110128SFei.Feng@Sun.COM uint8_t flags;
12210128SFei.Feng@Sun.COM } uath_devs[] = {
12310128SFei.Feng@Sun.COM UATH_DEV_UG(ACCTON, SMCWUSBTG2),
12410128SFei.Feng@Sun.COM UATH_DEV_UG(ATHEROS, AR5523),
12510128SFei.Feng@Sun.COM UATH_DEV_UG(ATHEROS2, AR5523_1),
12610128SFei.Feng@Sun.COM UATH_DEV_UG(ATHEROS2, AR5523_2),
12710128SFei.Feng@Sun.COM UATH_DEV_UX(ATHEROS2, AR5523_3),
12810128SFei.Feng@Sun.COM UATH_DEV_UG(CONCEPTRONIC, AR5523_1),
12910128SFei.Feng@Sun.COM UATH_DEV_UX(CONCEPTRONIC, AR5523_2),
13010128SFei.Feng@Sun.COM UATH_DEV_UX(DLINK, DWLAG122),
13110128SFei.Feng@Sun.COM UATH_DEV_UX(DLINK, DWLAG132),
13210128SFei.Feng@Sun.COM UATH_DEV_UG(DLINK, DWLG132),
13310128SFei.Feng@Sun.COM UATH_DEV_UG(GIGASET, AR5523),
13410128SFei.Feng@Sun.COM UATH_DEV_UG(GIGASET, SMCWUSBTG),
13510128SFei.Feng@Sun.COM UATH_DEV_UG(GLOBALSUN, AR5523_1),
13610128SFei.Feng@Sun.COM UATH_DEV_UX(GLOBALSUN, AR5523_2),
13710128SFei.Feng@Sun.COM UATH_DEV_UG(IODATA, USBWNG54US),
13810128SFei.Feng@Sun.COM UATH_DEV_UG(MELCO, WLIU2KAMG54),
13910128SFei.Feng@Sun.COM UATH_DEV_UX(NETGEAR, WG111U),
14010128SFei.Feng@Sun.COM UATH_DEV_UG(NETGEAR3, WG111T),
14110128SFei.Feng@Sun.COM UATH_DEV_UG(NETGEAR3, WPN111),
14210128SFei.Feng@Sun.COM UATH_DEV_UG(PHILIPS, SNU6500),
14310128SFei.Feng@Sun.COM UATH_DEV_UX(UMEDIA, AR5523_2),
14410128SFei.Feng@Sun.COM UATH_DEV_UG(UMEDIA, TEW444UBEU),
14510128SFei.Feng@Sun.COM UATH_DEV_UG(WISTRONNEWEB, AR5523_1),
14610128SFei.Feng@Sun.COM UATH_DEV_UX(WISTRONNEWEB, AR5523_2),
14710128SFei.Feng@Sun.COM UATH_DEV_UG(ZCOM, AR5523)
14810128SFei.Feng@Sun.COM };
14910128SFei.Feng@Sun.COM
15010128SFei.Feng@Sun.COM static char uath_fwmod[] = "uathfw";
15110128SFei.Feng@Sun.COM static char uath_binmod[] = "uathbin";
15210128SFei.Feng@Sun.COM
15310128SFei.Feng@Sun.COM /*
15410128SFei.Feng@Sun.COM * Supported rates for 802.11b/g modes (in 500Kbps unit).
15510128SFei.Feng@Sun.COM */
15610128SFei.Feng@Sun.COM static const struct ieee80211_rateset uath_rateset_11b =
15710128SFei.Feng@Sun.COM { 4, { 2, 4, 11, 22 } };
15810128SFei.Feng@Sun.COM
15910128SFei.Feng@Sun.COM static const struct ieee80211_rateset uath_rateset_11g =
16010128SFei.Feng@Sun.COM { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
16110128SFei.Feng@Sun.COM
16210128SFei.Feng@Sun.COM /*
16310128SFei.Feng@Sun.COM * device operations
16410128SFei.Feng@Sun.COM */
16510128SFei.Feng@Sun.COM static int uath_attach(dev_info_t *, ddi_attach_cmd_t);
16610128SFei.Feng@Sun.COM static int uath_detach(dev_info_t *, ddi_detach_cmd_t);
16710128SFei.Feng@Sun.COM
16810128SFei.Feng@Sun.COM /*
16910128SFei.Feng@Sun.COM * Module Loading Data & Entry Points
17010128SFei.Feng@Sun.COM */
17110128SFei.Feng@Sun.COM DDI_DEFINE_STREAM_OPS(uath_dev_ops, nulldev, nulldev, uath_attach,
17210128SFei.Feng@Sun.COM uath_detach, nodev, NULL, D_MP, NULL, ddi_quiesce_not_needed);
17310128SFei.Feng@Sun.COM
17410128SFei.Feng@Sun.COM static struct modldrv uath_modldrv = {
17510128SFei.Feng@Sun.COM &mod_driverops, /* Type of module. This one is a driver */
17610128SFei.Feng@Sun.COM "Atheros AR5523 USB Driver v1.1", /* short description */
17710128SFei.Feng@Sun.COM &uath_dev_ops /* driver specific ops */
17810128SFei.Feng@Sun.COM };
17910128SFei.Feng@Sun.COM
18010128SFei.Feng@Sun.COM static struct modlinkage modlinkage = {
18110128SFei.Feng@Sun.COM MODREV_1,
18210128SFei.Feng@Sun.COM (void *)&uath_modldrv,
18310128SFei.Feng@Sun.COM NULL
18410128SFei.Feng@Sun.COM };
18510128SFei.Feng@Sun.COM
18610128SFei.Feng@Sun.COM static int uath_m_stat(void *, uint_t, uint64_t *);
18710128SFei.Feng@Sun.COM static int uath_m_start(void *);
18810128SFei.Feng@Sun.COM static void uath_m_stop(void *);
18910128SFei.Feng@Sun.COM static int uath_m_promisc(void *, boolean_t);
19010128SFei.Feng@Sun.COM static int uath_m_multicst(void *, boolean_t, const uint8_t *);
19110128SFei.Feng@Sun.COM static int uath_m_unicst(void *, const uint8_t *);
19210128SFei.Feng@Sun.COM static mblk_t *uath_m_tx(void *, mblk_t *);
19310128SFei.Feng@Sun.COM static void uath_m_ioctl(void *, queue_t *, mblk_t *);
19410128SFei.Feng@Sun.COM static int uath_m_setprop(void *, const char *, mac_prop_id_t,
19510128SFei.Feng@Sun.COM uint_t, const void *);
19610128SFei.Feng@Sun.COM static int uath_m_getprop(void *, const char *, mac_prop_id_t,
197*11878SVenu.Iyer@Sun.COM uint_t, void *);
198*11878SVenu.Iyer@Sun.COM static void uath_m_propinfo(void *, const char *, mac_prop_id_t,
199*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t);
20010128SFei.Feng@Sun.COM
20110128SFei.Feng@Sun.COM static mac_callbacks_t uath_m_callbacks = {
202*11878SVenu.Iyer@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
20310128SFei.Feng@Sun.COM uath_m_stat,
20410128SFei.Feng@Sun.COM uath_m_start,
20510128SFei.Feng@Sun.COM uath_m_stop,
20610128SFei.Feng@Sun.COM uath_m_promisc,
20710128SFei.Feng@Sun.COM uath_m_multicst,
20810128SFei.Feng@Sun.COM uath_m_unicst,
20910128SFei.Feng@Sun.COM uath_m_tx,
210*11878SVenu.Iyer@Sun.COM NULL,
21110128SFei.Feng@Sun.COM uath_m_ioctl,
21210128SFei.Feng@Sun.COM NULL,
21310128SFei.Feng@Sun.COM NULL,
21410128SFei.Feng@Sun.COM NULL,
21510128SFei.Feng@Sun.COM uath_m_setprop,
216*11878SVenu.Iyer@Sun.COM uath_m_getprop,
217*11878SVenu.Iyer@Sun.COM uath_m_propinfo
21810128SFei.Feng@Sun.COM };
21910128SFei.Feng@Sun.COM
22010128SFei.Feng@Sun.COM static usb_alt_if_data_t *
22110128SFei.Feng@Sun.COM uath_lookup_alt_if(usb_client_dev_data_t *,
22210128SFei.Feng@Sun.COM uint_t, uint_t, uint_t);
22310128SFei.Feng@Sun.COM static usb_ep_data_t *
22410128SFei.Feng@Sun.COM uath_lookup_ep_data(dev_info_t *,
22510128SFei.Feng@Sun.COM usb_client_dev_data_t *, uint_t, uint_t, uint8_t, uint8_t);
22610128SFei.Feng@Sun.COM static const char *
22710128SFei.Feng@Sun.COM uath_codename(int code);
22810128SFei.Feng@Sun.COM
22910128SFei.Feng@Sun.COM static uint_t uath_lookup(uint16_t, uint16_t);
23010128SFei.Feng@Sun.COM static void uath_list_all_eps(usb_alt_if_data_t *);
23110128SFei.Feng@Sun.COM static int uath_open_pipes(struct uath_softc *);
23210128SFei.Feng@Sun.COM static void uath_close_pipes(struct uath_softc *);
23310128SFei.Feng@Sun.COM static int uath_fw_send(struct uath_softc *,
23410128SFei.Feng@Sun.COM usb_pipe_handle_t, const void *, size_t);
23510128SFei.Feng@Sun.COM static int uath_fw_ack(struct uath_softc *, int);
23610128SFei.Feng@Sun.COM static int uath_loadsym(ddi_modhandle_t, char *, char **, size_t *);
23710128SFei.Feng@Sun.COM static int uath_loadfirmware(struct uath_softc *);
23810128SFei.Feng@Sun.COM static int uath_alloc_cmd_list(struct uath_softc *,
23910128SFei.Feng@Sun.COM struct uath_cmd *, int, int);
24010128SFei.Feng@Sun.COM static int uath_init_cmd_list(struct uath_softc *);
24110128SFei.Feng@Sun.COM static void uath_free_cmd_list(struct uath_cmd *, int);
24210128SFei.Feng@Sun.COM static int uath_host_available(struct uath_softc *);
24310128SFei.Feng@Sun.COM static void uath_get_capability(struct uath_softc *, uint32_t, uint32_t *);
24410128SFei.Feng@Sun.COM static int uath_get_devcap(struct uath_softc *);
24510128SFei.Feng@Sun.COM static int uath_get_devstatus(struct uath_softc *, uint8_t *);
24610128SFei.Feng@Sun.COM static int uath_get_status(struct uath_softc *, uint32_t, void *, int);
24710128SFei.Feng@Sun.COM
24810128SFei.Feng@Sun.COM static void uath_cmd_lock_init(struct uath_cmd_lock *);
24910128SFei.Feng@Sun.COM static void uath_cmd_lock_destroy(struct uath_cmd_lock *);
25010128SFei.Feng@Sun.COM static int uath_cmd_lock_wait(struct uath_cmd_lock *, clock_t);
25110128SFei.Feng@Sun.COM static void uath_cmd_lock_signal(struct uath_cmd_lock *);
25210128SFei.Feng@Sun.COM
25310128SFei.Feng@Sun.COM static int uath_cmd_read(struct uath_softc *, uint32_t, const void *,
25410128SFei.Feng@Sun.COM int, void *, int, int);
25510128SFei.Feng@Sun.COM static int uath_cmd_write(struct uath_softc *, uint32_t, const void *,
25610128SFei.Feng@Sun.COM int, int);
25710128SFei.Feng@Sun.COM static int uath_cmdsend(struct uath_softc *, uint32_t,
25810128SFei.Feng@Sun.COM const void *, int, void *, int, int);
25910128SFei.Feng@Sun.COM static int uath_rx_cmd_xfer(struct uath_softc *);
26010128SFei.Feng@Sun.COM static int uath_tx_cmd_xfer(struct uath_softc *,
26110128SFei.Feng@Sun.COM usb_pipe_handle_t, const void *, uint_t);
26210128SFei.Feng@Sun.COM static void uath_cmd_txeof(usb_pipe_handle_t, struct usb_bulk_req *);
26310128SFei.Feng@Sun.COM static void uath_cmd_rxeof(usb_pipe_handle_t, usb_bulk_req_t *);
26410128SFei.Feng@Sun.COM static void uath_cmdeof(struct uath_softc *, struct uath_cmd *);
26510128SFei.Feng@Sun.COM
26610128SFei.Feng@Sun.COM static void uath_init_data_queue(struct uath_softc *);
26710128SFei.Feng@Sun.COM static int uath_rx_data_xfer(struct uath_softc *sc);
26810128SFei.Feng@Sun.COM static int uath_tx_data_xfer(struct uath_softc *, mblk_t *);
26910128SFei.Feng@Sun.COM static void uath_data_txeof(usb_pipe_handle_t, usb_bulk_req_t *);
27010128SFei.Feng@Sun.COM static void uath_data_rxeof(usb_pipe_handle_t, usb_bulk_req_t *);
27110128SFei.Feng@Sun.COM
27210128SFei.Feng@Sun.COM static int uath_create_connection(struct uath_softc *, uint32_t);
27310128SFei.Feng@Sun.COM static int uath_set_rates(struct uath_softc *,
27410128SFei.Feng@Sun.COM const struct ieee80211_rateset *);
27510128SFei.Feng@Sun.COM static int uath_write_associd(struct uath_softc *);
27610128SFei.Feng@Sun.COM static int uath_set_ledsteady(struct uath_softc *, int, int);
27710128SFei.Feng@Sun.COM static int uath_set_ledblink(struct uath_softc *, int, int, int, int);
27810128SFei.Feng@Sun.COM static void uath_update_rxstat(struct uath_softc *, uint32_t);
27910128SFei.Feng@Sun.COM static int uath_send(ieee80211com_t *, mblk_t *, uint8_t);
28010128SFei.Feng@Sun.COM static int uath_reconnect(dev_info_t *);
28110128SFei.Feng@Sun.COM static int uath_disconnect(dev_info_t *);
28210128SFei.Feng@Sun.COM static int uath_newstate(struct ieee80211com *, enum ieee80211_state, int);
28310128SFei.Feng@Sun.COM
28410128SFei.Feng@Sun.COM static int uath_dataflush(struct uath_softc *);
28510128SFei.Feng@Sun.COM static int uath_cmdflush(struct uath_softc *);
28610128SFei.Feng@Sun.COM static int uath_flush(struct uath_softc *);
28710128SFei.Feng@Sun.COM static int uath_set_ledstate(struct uath_softc *, int);
28810128SFei.Feng@Sun.COM static int uath_set_chan(struct uath_softc *, struct ieee80211_channel *);
28910128SFei.Feng@Sun.COM static int uath_reset_tx_queues(struct uath_softc *);
29010128SFei.Feng@Sun.COM static int uath_wme_init(struct uath_softc *);
29110128SFei.Feng@Sun.COM static int uath_config_multi(struct uath_softc *,
29210128SFei.Feng@Sun.COM uint32_t, const void *, int);
29310128SFei.Feng@Sun.COM static void uath_config(struct uath_softc *, uint32_t, uint32_t);
29410128SFei.Feng@Sun.COM static int uath_switch_channel(struct uath_softc *,
29510128SFei.Feng@Sun.COM struct ieee80211_channel *);
29610128SFei.Feng@Sun.COM static int uath_set_rxfilter(struct uath_softc *, uint32_t, uint32_t);
29710128SFei.Feng@Sun.COM static int uath_init_locked(void *);
29810128SFei.Feng@Sun.COM static void uath_stop_locked(void *);
29910128SFei.Feng@Sun.COM static int uath_init(struct uath_softc *);
30010128SFei.Feng@Sun.COM static void uath_stop(struct uath_softc *);
30110128SFei.Feng@Sun.COM static void uath_resume(struct uath_softc *);
30210128SFei.Feng@Sun.COM
30310128SFei.Feng@Sun.COM static void
uath_debug(uint32_t dbg_flags,const int8_t * fmt,...)30410128SFei.Feng@Sun.COM uath_debug(uint32_t dbg_flags, const int8_t *fmt, ...)
30510128SFei.Feng@Sun.COM {
30610128SFei.Feng@Sun.COM va_list args;
30710128SFei.Feng@Sun.COM
30810128SFei.Feng@Sun.COM if (dbg_flags & uath_dbg_flags) {
30910128SFei.Feng@Sun.COM va_start(args, fmt);
31010128SFei.Feng@Sun.COM vcmn_err(CE_CONT, fmt, args);
31110128SFei.Feng@Sun.COM va_end(args);
31210128SFei.Feng@Sun.COM }
31310128SFei.Feng@Sun.COM }
31410128SFei.Feng@Sun.COM
31510128SFei.Feng@Sun.COM static uint_t
uath_lookup(uint16_t vendor_id,uint16_t product_id)31610128SFei.Feng@Sun.COM uath_lookup(uint16_t vendor_id, uint16_t product_id)
31710128SFei.Feng@Sun.COM {
31810128SFei.Feng@Sun.COM int i, size;
31910128SFei.Feng@Sun.COM
32010128SFei.Feng@Sun.COM size = sizeof (uath_devs) / sizeof (struct uath_type);
32110128SFei.Feng@Sun.COM
32210128SFei.Feng@Sun.COM for (i = 0; i < size; i++) {
32310128SFei.Feng@Sun.COM if ((vendor_id == uath_devs[i].dev.vendor_id) &&
32410128SFei.Feng@Sun.COM (product_id == uath_devs[i].dev.product_id))
32510128SFei.Feng@Sun.COM return (uath_devs[i].flags);
32610128SFei.Feng@Sun.COM }
32710128SFei.Feng@Sun.COM return (UATH_FLAG_ERR);
32810128SFei.Feng@Sun.COM }
32910128SFei.Feng@Sun.COM
33010128SFei.Feng@Sun.COM /*
33110128SFei.Feng@Sun.COM * Return a specific alt_if from the device descriptor tree.
33210128SFei.Feng@Sun.COM */
33310128SFei.Feng@Sun.COM static usb_alt_if_data_t *
uath_lookup_alt_if(usb_client_dev_data_t * dev_data,uint_t config,uint_t interface,uint_t alt)33410128SFei.Feng@Sun.COM uath_lookup_alt_if(usb_client_dev_data_t *dev_data, uint_t config,
33510128SFei.Feng@Sun.COM uint_t interface, uint_t alt)
33610128SFei.Feng@Sun.COM {
33710128SFei.Feng@Sun.COM usb_cfg_data_t *cfg_data;
33810128SFei.Feng@Sun.COM usb_if_data_t *if_data;
33910128SFei.Feng@Sun.COM usb_alt_if_data_t *if_alt_data;
34010128SFei.Feng@Sun.COM
34110128SFei.Feng@Sun.COM /*
34210128SFei.Feng@Sun.COM * Assume everything is in the tree for now,
34310128SFei.Feng@Sun.COM * (USB_PARSE_LVL_ALL)
34410128SFei.Feng@Sun.COM * so we can directly index the array.
34510128SFei.Feng@Sun.COM */
34610128SFei.Feng@Sun.COM
34710128SFei.Feng@Sun.COM /* Descend to configuration, configs are 1-based */
34810128SFei.Feng@Sun.COM if (config < 1 || config > dev_data->dev_n_cfg)
34910128SFei.Feng@Sun.COM return (NULL);
35010128SFei.Feng@Sun.COM cfg_data = &dev_data->dev_cfg[config - 1];
35110128SFei.Feng@Sun.COM
35210128SFei.Feng@Sun.COM /* Descend to interface */
35310128SFei.Feng@Sun.COM if (interface > cfg_data->cfg_n_if - 1)
35410128SFei.Feng@Sun.COM return (NULL);
35510128SFei.Feng@Sun.COM if_data = &cfg_data->cfg_if[interface];
35610128SFei.Feng@Sun.COM
35710128SFei.Feng@Sun.COM /* Descend to alt */
35810128SFei.Feng@Sun.COM if (alt > if_data->if_n_alt - 1)
35910128SFei.Feng@Sun.COM return (NULL);
36010128SFei.Feng@Sun.COM if_alt_data = &if_data->if_alt[alt];
36110128SFei.Feng@Sun.COM
36210128SFei.Feng@Sun.COM return (if_alt_data);
36310128SFei.Feng@Sun.COM }
36410128SFei.Feng@Sun.COM
36510128SFei.Feng@Sun.COM /*
36610128SFei.Feng@Sun.COM * Print all endpoints of an alt_if.
36710128SFei.Feng@Sun.COM */
36810128SFei.Feng@Sun.COM static void
uath_list_all_eps(usb_alt_if_data_t * ifalt)36910128SFei.Feng@Sun.COM uath_list_all_eps(usb_alt_if_data_t *ifalt)
37010128SFei.Feng@Sun.COM {
37110128SFei.Feng@Sun.COM usb_ep_data_t *ep_data;
37210128SFei.Feng@Sun.COM usb_ep_descr_t *ep_descr;
37310128SFei.Feng@Sun.COM int i;
37410128SFei.Feng@Sun.COM
37510128SFei.Feng@Sun.COM for (i = 0; i < ifalt->altif_n_ep; i++) {
37610128SFei.Feng@Sun.COM ep_data = &ifalt->altif_ep[i];
37710128SFei.Feng@Sun.COM ep_descr = &ep_data->ep_descr;
37810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_USB,
37910128SFei.Feng@Sun.COM "uath: uath_list_all_endpoint: "
38010128SFei.Feng@Sun.COM "ep addresa[%x] is %x",
38110128SFei.Feng@Sun.COM i, ep_descr->bEndpointAddress);
38210128SFei.Feng@Sun.COM }
38310128SFei.Feng@Sun.COM }
38410128SFei.Feng@Sun.COM
38510128SFei.Feng@Sun.COM static usb_ep_data_t *
uath_lookup_ep_data(dev_info_t * dip,usb_client_dev_data_t * dev_datap,uint_t interface,uint_t alternate,uint8_t address,uint8_t type)38610128SFei.Feng@Sun.COM uath_lookup_ep_data(dev_info_t *dip,
38710128SFei.Feng@Sun.COM usb_client_dev_data_t *dev_datap,
38810128SFei.Feng@Sun.COM uint_t interface,
38910128SFei.Feng@Sun.COM uint_t alternate,
39010128SFei.Feng@Sun.COM uint8_t address,
39110128SFei.Feng@Sun.COM uint8_t type)
39210128SFei.Feng@Sun.COM {
39310128SFei.Feng@Sun.COM usb_alt_if_data_t *altif_data;
39410128SFei.Feng@Sun.COM int i;
39510128SFei.Feng@Sun.COM
39610128SFei.Feng@Sun.COM if ((dip == NULL) || (dev_datap == NULL))
39710128SFei.Feng@Sun.COM return (NULL);
39810128SFei.Feng@Sun.COM
39910128SFei.Feng@Sun.COM altif_data = &dev_datap->dev_curr_cfg->
40010128SFei.Feng@Sun.COM cfg_if[interface].if_alt[alternate];
40110128SFei.Feng@Sun.COM
40210128SFei.Feng@Sun.COM for (i = 0; i < altif_data->altif_n_ep; i++) {
40310128SFei.Feng@Sun.COM usb_ep_descr_t *ept = &altif_data->altif_ep[i].ep_descr;
40410128SFei.Feng@Sun.COM uint8_t ept_type = ept->bmAttributes & USB_EP_ATTR_MASK;
40510128SFei.Feng@Sun.COM uint8_t ept_address = ept->bEndpointAddress;
40610128SFei.Feng@Sun.COM
40710128SFei.Feng@Sun.COM if (ept->bLength == 0)
40810128SFei.Feng@Sun.COM continue;
40910128SFei.Feng@Sun.COM if ((ept_type == type) &&
41010128SFei.Feng@Sun.COM ((type == USB_EP_ATTR_CONTROL) || (address == ept_address)))
41110128SFei.Feng@Sun.COM return (&altif_data->altif_ep[i]);
41210128SFei.Feng@Sun.COM }
41310128SFei.Feng@Sun.COM return (NULL);
41410128SFei.Feng@Sun.COM }
41510128SFei.Feng@Sun.COM
41610128SFei.Feng@Sun.COM /*
41710128SFei.Feng@Sun.COM * Open communication pipes.
41810128SFei.Feng@Sun.COM * The following pipes are used by the AR5523:
41910128SFei.Feng@Sun.COM * ep0: 0x81 IN Rx cmd
42010128SFei.Feng@Sun.COM * ep1: 0x01 OUT Tx cmd
42110128SFei.Feng@Sun.COM * ep2: 0x82 IN Rx data
42210128SFei.Feng@Sun.COM * ep3: 0x02 OUT Tx data
42310128SFei.Feng@Sun.COM */
42410128SFei.Feng@Sun.COM static int
uath_open_pipes(struct uath_softc * sc)42510128SFei.Feng@Sun.COM uath_open_pipes(struct uath_softc *sc)
42610128SFei.Feng@Sun.COM {
42710128SFei.Feng@Sun.COM usb_ep_data_t *ep_node;
42810128SFei.Feng@Sun.COM usb_ep_descr_t *ep_descr;
42910128SFei.Feng@Sun.COM usb_pipe_policy_t policy;
43010128SFei.Feng@Sun.COM int err;
43110128SFei.Feng@Sun.COM
43210128SFei.Feng@Sun.COM #ifdef DEBUG
43310128SFei.Feng@Sun.COM usb_alt_if_data_t *altif_data;
43410128SFei.Feng@Sun.COM
43510128SFei.Feng@Sun.COM altif_data = uath_lookup_alt_if(sc->sc_udev, UATH_CONFIG_NO,
43610128SFei.Feng@Sun.COM UATH_IFACE_INDEX, UATH_ALT_IF_INDEX);
43710128SFei.Feng@Sun.COM if (altif_data == NULL) {
43810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "alt_if not found");
43910128SFei.Feng@Sun.COM return (USB_FAILURE);
44010128SFei.Feng@Sun.COM }
44110128SFei.Feng@Sun.COM
44210128SFei.Feng@Sun.COM uath_list_all_eps(altif_data);
44310128SFei.Feng@Sun.COM #endif
44410128SFei.Feng@Sun.COM
44510128SFei.Feng@Sun.COM /*
44610128SFei.Feng@Sun.COM * XXX pipes numbers are hardcoded because we don't have any way
44710128SFei.Feng@Sun.COM * to distinguish the data pipes from the firmware command pipes
44810128SFei.Feng@Sun.COM * (both are bulk pipes) using the endpoints descriptors.
44910128SFei.Feng@Sun.COM */
45010128SFei.Feng@Sun.COM ep_node = uath_lookup_ep_data(sc->sc_dev, sc->sc_udev,
45110128SFei.Feng@Sun.COM 0, 0, 0x81, USB_EP_ATTR_BULK);
45210128SFei.Feng@Sun.COM ep_descr = &ep_node->ep_descr;
45310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_open_pipes(): "
45410128SFei.Feng@Sun.COM "find pipe %x\n", ep_descr->bEndpointAddress);
45510128SFei.Feng@Sun.COM
45610128SFei.Feng@Sun.COM bzero(&policy, sizeof (usb_pipe_policy_t));
45710128SFei.Feng@Sun.COM policy.pp_max_async_reqs = UATH_CMD_LIST_COUNT;
45810128SFei.Feng@Sun.COM
45910128SFei.Feng@Sun.COM err = usb_pipe_open(sc->sc_dev, &ep_node->ep_descr,
46010128SFei.Feng@Sun.COM &policy, USB_FLAGS_SLEEP, &sc->rx_cmd_pipe);
46110128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
46210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_open_pipes(): "
46310128SFei.Feng@Sun.COM "failed to open rx data pipe, err = %x\n",
46410128SFei.Feng@Sun.COM err);
46510128SFei.Feng@Sun.COM goto fail;
46610128SFei.Feng@Sun.COM }
46710128SFei.Feng@Sun.COM
46810128SFei.Feng@Sun.COM
46910128SFei.Feng@Sun.COM ep_node = uath_lookup_ep_data(sc->sc_dev, sc->sc_udev,
47010128SFei.Feng@Sun.COM 0, 0, 0x01, USB_EP_ATTR_BULK);
47110128SFei.Feng@Sun.COM ep_descr = &ep_node->ep_descr;
47210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_open_pipes(): "
47310128SFei.Feng@Sun.COM "find pipe %x\n",
47410128SFei.Feng@Sun.COM ep_descr->bEndpointAddress);
47510128SFei.Feng@Sun.COM
47610128SFei.Feng@Sun.COM bzero(&policy, sizeof (usb_pipe_policy_t));
47710128SFei.Feng@Sun.COM policy.pp_max_async_reqs = UATH_CMD_LIST_COUNT;
47810128SFei.Feng@Sun.COM
47910128SFei.Feng@Sun.COM err = usb_pipe_open(sc->sc_dev, &ep_node->ep_descr,
48010128SFei.Feng@Sun.COM &policy, USB_FLAGS_SLEEP, &sc->tx_cmd_pipe);
48110128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
48210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_open_pipes(): "
48310128SFei.Feng@Sun.COM "failed to open tx command pipe, err = %x\n",
48410128SFei.Feng@Sun.COM err);
48510128SFei.Feng@Sun.COM goto fail;
48610128SFei.Feng@Sun.COM }
48710128SFei.Feng@Sun.COM
48810128SFei.Feng@Sun.COM ep_node = uath_lookup_ep_data(sc->sc_dev, sc->sc_udev,
48910128SFei.Feng@Sun.COM 0, 0, 0x82, USB_EP_ATTR_BULK);
49010128SFei.Feng@Sun.COM ep_descr = &ep_node->ep_descr;
49110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_open_pipes(): "
49210128SFei.Feng@Sun.COM "find pipe %x\n",
49310128SFei.Feng@Sun.COM ep_descr->bEndpointAddress);
49410128SFei.Feng@Sun.COM
49510128SFei.Feng@Sun.COM bzero(&policy, sizeof (usb_pipe_policy_t));
49610128SFei.Feng@Sun.COM policy.pp_max_async_reqs = UATH_RX_DATA_LIST_COUNT;
49710128SFei.Feng@Sun.COM
49810128SFei.Feng@Sun.COM err = usb_pipe_open(sc->sc_dev, &ep_node->ep_descr,
49910128SFei.Feng@Sun.COM &policy, USB_FLAGS_SLEEP, &sc->rx_data_pipe);
50010128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
50110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_open_pipes(): "
50210128SFei.Feng@Sun.COM "failed to open tx pipe, err = %x\n",
50310128SFei.Feng@Sun.COM err);
50410128SFei.Feng@Sun.COM goto fail;
50510128SFei.Feng@Sun.COM }
50610128SFei.Feng@Sun.COM
50710128SFei.Feng@Sun.COM ep_node = uath_lookup_ep_data(sc->sc_dev, sc->sc_udev,
50810128SFei.Feng@Sun.COM 0, 0, 0x02, USB_EP_ATTR_BULK);
50910128SFei.Feng@Sun.COM ep_descr = &ep_node->ep_descr;
51010128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_open_pipes(): "
51110128SFei.Feng@Sun.COM "find pipe %x\n",
51210128SFei.Feng@Sun.COM ep_descr->bEndpointAddress);
51310128SFei.Feng@Sun.COM
51410128SFei.Feng@Sun.COM bzero(&policy, sizeof (usb_pipe_policy_t));
51510128SFei.Feng@Sun.COM policy.pp_max_async_reqs = UATH_TX_DATA_LIST_COUNT;
51610128SFei.Feng@Sun.COM
51710128SFei.Feng@Sun.COM err = usb_pipe_open(sc->sc_dev, &ep_node->ep_descr,
51810128SFei.Feng@Sun.COM &policy, USB_FLAGS_SLEEP, &sc->tx_data_pipe);
51910128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
52010128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_open_pipes(): "
52110128SFei.Feng@Sun.COM "failed to open rx command pipe, err = %x\n",
52210128SFei.Feng@Sun.COM err);
52310128SFei.Feng@Sun.COM goto fail;
52410128SFei.Feng@Sun.COM }
52510128SFei.Feng@Sun.COM
52610128SFei.Feng@Sun.COM return (UATH_SUCCESS);
52710128SFei.Feng@Sun.COM fail:
52810128SFei.Feng@Sun.COM uath_close_pipes(sc);
52910128SFei.Feng@Sun.COM return (err);
53010128SFei.Feng@Sun.COM }
53110128SFei.Feng@Sun.COM
53210128SFei.Feng@Sun.COM static void
uath_close_pipes(struct uath_softc * sc)53310128SFei.Feng@Sun.COM uath_close_pipes(struct uath_softc *sc)
53410128SFei.Feng@Sun.COM {
53510128SFei.Feng@Sun.COM usb_flags_t flags = USB_FLAGS_SLEEP;
53610128SFei.Feng@Sun.COM
53710128SFei.Feng@Sun.COM if (sc->rx_cmd_pipe != NULL) {
53810128SFei.Feng@Sun.COM usb_pipe_reset(sc->sc_dev, sc->rx_cmd_pipe, flags, NULL, 0);
53910128SFei.Feng@Sun.COM usb_pipe_close(sc->sc_dev, sc->rx_cmd_pipe, flags, NULL, 0);
54010128SFei.Feng@Sun.COM sc->rx_cmd_pipe = NULL;
54110128SFei.Feng@Sun.COM }
54210128SFei.Feng@Sun.COM
54310128SFei.Feng@Sun.COM if (sc->tx_cmd_pipe != NULL) {
54410128SFei.Feng@Sun.COM usb_pipe_reset(sc->sc_dev, sc->tx_cmd_pipe, flags, NULL, 0);
54510128SFei.Feng@Sun.COM usb_pipe_close(sc->sc_dev, sc->tx_cmd_pipe, flags, NULL, 0);
54610128SFei.Feng@Sun.COM sc->tx_cmd_pipe = NULL;
54710128SFei.Feng@Sun.COM }
54810128SFei.Feng@Sun.COM
54910128SFei.Feng@Sun.COM if (sc->rx_data_pipe != NULL) {
55010128SFei.Feng@Sun.COM usb_pipe_reset(sc->sc_dev, sc->rx_data_pipe, flags, NULL, 0);
55110128SFei.Feng@Sun.COM usb_pipe_close(sc->sc_dev, sc->rx_data_pipe, flags, NULL, 0);
55210128SFei.Feng@Sun.COM sc->rx_data_pipe = NULL;
55310128SFei.Feng@Sun.COM }
55410128SFei.Feng@Sun.COM
55510128SFei.Feng@Sun.COM if (sc->tx_data_pipe != NULL) {
55610128SFei.Feng@Sun.COM usb_pipe_reset(sc->sc_dev, sc->tx_data_pipe, flags, NULL, 0);
55710128SFei.Feng@Sun.COM usb_pipe_close(sc->sc_dev, sc->tx_data_pipe, flags, NULL, 0);
55810128SFei.Feng@Sun.COM sc->tx_data_pipe = NULL;
55910128SFei.Feng@Sun.COM }
56010128SFei.Feng@Sun.COM
56110128SFei.Feng@Sun.COM }
56210128SFei.Feng@Sun.COM
56310128SFei.Feng@Sun.COM static const char *
uath_codename(int code)56410128SFei.Feng@Sun.COM uath_codename(int code)
56510128SFei.Feng@Sun.COM {
56610128SFei.Feng@Sun.COM #define N(a) (sizeof (a)/sizeof (a[0]))
56710128SFei.Feng@Sun.COM static const char *names[] = {
56810128SFei.Feng@Sun.COM "0x00",
56910128SFei.Feng@Sun.COM "HOST_AVAILABLE",
57010128SFei.Feng@Sun.COM "BIND",
57110128SFei.Feng@Sun.COM "TARGET_RESET",
57210128SFei.Feng@Sun.COM "TARGET_GET_CAPABILITY",
57310128SFei.Feng@Sun.COM "TARGET_SET_CONFIG",
57410128SFei.Feng@Sun.COM "TARGET_GET_STATUS",
57510128SFei.Feng@Sun.COM "TARGET_GET_STATS",
57610128SFei.Feng@Sun.COM "TARGET_START",
57710128SFei.Feng@Sun.COM "TARGET_STOP",
57810128SFei.Feng@Sun.COM "TARGET_ENABLE",
57910128SFei.Feng@Sun.COM "TARGET_DISABLE",
58010128SFei.Feng@Sun.COM "CREATE_CONNECTION",
58110128SFei.Feng@Sun.COM "UPDATE_CONNECT_ATTR",
58210128SFei.Feng@Sun.COM "DELETE_CONNECT",
58310128SFei.Feng@Sun.COM "SEND",
58410128SFei.Feng@Sun.COM "FLUSH",
58510128SFei.Feng@Sun.COM "STATS_UPDATE",
58610128SFei.Feng@Sun.COM "BMISS",
58710128SFei.Feng@Sun.COM "DEVICE_AVAIL",
58810128SFei.Feng@Sun.COM "SEND_COMPLETE",
58910128SFei.Feng@Sun.COM "DATA_AVAIL",
59010128SFei.Feng@Sun.COM "SET_PWR_MODE",
59110128SFei.Feng@Sun.COM "BMISS_ACK",
59210128SFei.Feng@Sun.COM "SET_LED_STEADY",
59310128SFei.Feng@Sun.COM "SET_LED_BLINK",
59410128SFei.Feng@Sun.COM "SETUP_BEACON_DESC",
59510128SFei.Feng@Sun.COM "BEACON_INIT",
59610128SFei.Feng@Sun.COM "RESET_KEY_CACHE",
59710128SFei.Feng@Sun.COM "RESET_KEY_CACHE_ENTRY",
59810128SFei.Feng@Sun.COM "SET_KEY_CACHE_ENTRY",
59910128SFei.Feng@Sun.COM "SET_DECOMP_MASK",
60010128SFei.Feng@Sun.COM "SET_REGULATORY_DOMAIN",
60110128SFei.Feng@Sun.COM "SET_LED_STATE",
60210128SFei.Feng@Sun.COM "WRITE_ASSOCID",
60310128SFei.Feng@Sun.COM "SET_STA_BEACON_TIMERS",
60410128SFei.Feng@Sun.COM "GET_TSF",
60510128SFei.Feng@Sun.COM "RESET_TSF",
60610128SFei.Feng@Sun.COM "SET_ADHOC_MODE",
60710128SFei.Feng@Sun.COM "SET_BASIC_RATE",
60810128SFei.Feng@Sun.COM "MIB_CONTROL",
60910128SFei.Feng@Sun.COM "GET_CHANNEL_DATA",
61010128SFei.Feng@Sun.COM "GET_CUR_RSSI",
61110128SFei.Feng@Sun.COM "SET_ANTENNA_SWITCH",
61210128SFei.Feng@Sun.COM "0x2c", "0x2d", "0x2e",
61310128SFei.Feng@Sun.COM "USE_SHORT_SLOT_TIME",
61410128SFei.Feng@Sun.COM "SET_POWER_MODE",
61510128SFei.Feng@Sun.COM "SETUP_PSPOLL_DESC",
61610128SFei.Feng@Sun.COM "SET_RX_MULTICAST_FILTER",
61710128SFei.Feng@Sun.COM "RX_FILTER",
61810128SFei.Feng@Sun.COM "PER_CALIBRATION",
61910128SFei.Feng@Sun.COM "RESET",
62010128SFei.Feng@Sun.COM "DISABLE",
62110128SFei.Feng@Sun.COM "PHY_DISABLE",
62210128SFei.Feng@Sun.COM "SET_TX_POWER_LIMIT",
62310128SFei.Feng@Sun.COM "SET_TX_QUEUE_PARAMS",
62410128SFei.Feng@Sun.COM "SETUP_TX_QUEUE",
62510128SFei.Feng@Sun.COM "RELEASE_TX_QUEUE",
62610128SFei.Feng@Sun.COM };
62710128SFei.Feng@Sun.COM static char buf[8];
62810128SFei.Feng@Sun.COM
62910128SFei.Feng@Sun.COM if (code < N(names))
63010128SFei.Feng@Sun.COM return (names[code]);
63110128SFei.Feng@Sun.COM if (code == WDCMSG_SET_DEFAULT_KEY)
63210128SFei.Feng@Sun.COM return ("SET_DEFAULT_KEY");
63310128SFei.Feng@Sun.COM
63410128SFei.Feng@Sun.COM (void) snprintf(buf, sizeof (buf), "0x%02x", code);
63510128SFei.Feng@Sun.COM return (buf);
63610128SFei.Feng@Sun.COM #undef N
63710128SFei.Feng@Sun.COM }
63810128SFei.Feng@Sun.COM
63910128SFei.Feng@Sun.COM static int
uath_fw_send(struct uath_softc * sc,usb_pipe_handle_t pipe,const void * data,size_t len)64010128SFei.Feng@Sun.COM uath_fw_send(struct uath_softc *sc, usb_pipe_handle_t pipe,
64110128SFei.Feng@Sun.COM const void *data, size_t len)
64210128SFei.Feng@Sun.COM {
64310128SFei.Feng@Sun.COM usb_bulk_req_t *send_req;
64410128SFei.Feng@Sun.COM mblk_t *mblk;
64510128SFei.Feng@Sun.COM int res;
64610128SFei.Feng@Sun.COM
64710128SFei.Feng@Sun.COM send_req = usb_alloc_bulk_req(sc->sc_dev, len, USB_FLAGS_SLEEP);
64810128SFei.Feng@Sun.COM
64910128SFei.Feng@Sun.COM send_req->bulk_len = (int)len;
65010128SFei.Feng@Sun.COM send_req->bulk_attributes = USB_ATTRS_AUTOCLEARING;
65110128SFei.Feng@Sun.COM send_req->bulk_timeout = UATH_CMD_TIMEOUT;
65210128SFei.Feng@Sun.COM
65310128SFei.Feng@Sun.COM mblk = send_req->bulk_data;
65410128SFei.Feng@Sun.COM bcopy(data, mblk->b_wptr, len);
65510128SFei.Feng@Sun.COM mblk->b_wptr += len;
65610128SFei.Feng@Sun.COM
65710128SFei.Feng@Sun.COM res = usb_pipe_bulk_xfer(pipe, send_req, USB_FLAGS_SLEEP);
65810128SFei.Feng@Sun.COM if (res != USB_SUCCESS) {
65910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_FW, "uath: uath_fw_send(): "
66010128SFei.Feng@Sun.COM "Error %x writing data to bulk/out pipe", res);
66110128SFei.Feng@Sun.COM return (UATH_FAILURE);
66210128SFei.Feng@Sun.COM }
66310128SFei.Feng@Sun.COM
66410128SFei.Feng@Sun.COM usb_free_bulk_req(send_req);
66510128SFei.Feng@Sun.COM return (UATH_SUCCESS);
66610128SFei.Feng@Sun.COM }
66710128SFei.Feng@Sun.COM
66810128SFei.Feng@Sun.COM static int
uath_fw_ack(struct uath_softc * sc,int len)66910128SFei.Feng@Sun.COM uath_fw_ack(struct uath_softc *sc, int len)
67010128SFei.Feng@Sun.COM {
67110128SFei.Feng@Sun.COM struct uath_fwblock *rxblock;
67210128SFei.Feng@Sun.COM usb_bulk_req_t *req;
67310128SFei.Feng@Sun.COM mblk_t *mp;
67410128SFei.Feng@Sun.COM int err;
67510128SFei.Feng@Sun.COM
67610128SFei.Feng@Sun.COM req = usb_alloc_bulk_req(sc->sc_dev, len, USB_FLAGS_SLEEP);
67710128SFei.Feng@Sun.COM if (req == NULL) {
67810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_FW,
67910128SFei.Feng@Sun.COM "uath: uath_fw_ack(): "
68010128SFei.Feng@Sun.COM "uath_rx_transfer(): failed to allocate req");
68110128SFei.Feng@Sun.COM return (UATH_FAILURE);
68210128SFei.Feng@Sun.COM }
68310128SFei.Feng@Sun.COM
68410128SFei.Feng@Sun.COM req->bulk_len = len;
68510128SFei.Feng@Sun.COM req->bulk_client_private = (usb_opaque_t)sc;
68610128SFei.Feng@Sun.COM req->bulk_timeout = 0;
68710128SFei.Feng@Sun.COM req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK
68810128SFei.Feng@Sun.COM | USB_ATTRS_AUTOCLEARING;
68910128SFei.Feng@Sun.COM
69010128SFei.Feng@Sun.COM err = usb_pipe_bulk_xfer(sc->rx_cmd_pipe, req, USB_FLAGS_SLEEP);
69110128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
69210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_FW, "uath: uath_fw_ack(): "
69310128SFei.Feng@Sun.COM "failed to do rx xfer, %d", err);
69410128SFei.Feng@Sun.COM usb_free_bulk_req(req);
69510128SFei.Feng@Sun.COM return (UATH_FAILURE);
69610128SFei.Feng@Sun.COM }
69710128SFei.Feng@Sun.COM
69810128SFei.Feng@Sun.COM mp = req->bulk_data;
69910128SFei.Feng@Sun.COM req->bulk_data = NULL;
70010128SFei.Feng@Sun.COM
70110128SFei.Feng@Sun.COM rxblock = (struct uath_fwblock *)mp->b_rptr;
70210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_FW, "uath: uath_fw_ack() "
70310128SFei.Feng@Sun.COM "rxblock flags=0x%x total=%d\n",
70410128SFei.Feng@Sun.COM BE_32(rxblock->flags), BE_32(rxblock->rxtotal));
70510128SFei.Feng@Sun.COM
70610128SFei.Feng@Sun.COM freemsg(mp);
70710128SFei.Feng@Sun.COM usb_free_bulk_req(req);
70810128SFei.Feng@Sun.COM
70910128SFei.Feng@Sun.COM return (UATH_SUCCESS);
71010128SFei.Feng@Sun.COM }
71110128SFei.Feng@Sun.COM
71210128SFei.Feng@Sun.COM /*
71310128SFei.Feng@Sun.COM * find uath firmware module's "_start" "_end" symbols
71410128SFei.Feng@Sun.COM * and get its size.
71510128SFei.Feng@Sun.COM */
71610128SFei.Feng@Sun.COM static int
uath_loadsym(ddi_modhandle_t modp,char * sym,char ** start,size_t * len)71710128SFei.Feng@Sun.COM uath_loadsym(ddi_modhandle_t modp, char *sym, char **start, size_t *len)
71810128SFei.Feng@Sun.COM {
71910128SFei.Feng@Sun.COM char start_sym[64];
72010128SFei.Feng@Sun.COM char end_sym[64];
72110128SFei.Feng@Sun.COM char *p, *end;
72210128SFei.Feng@Sun.COM int rv;
72310128SFei.Feng@Sun.COM size_t n;
72410128SFei.Feng@Sun.COM
72510128SFei.Feng@Sun.COM (void) snprintf(start_sym, sizeof (start_sym), "%s_start", sym);
72610128SFei.Feng@Sun.COM (void) snprintf(end_sym, sizeof (end_sym), "%s_end", sym);
72710128SFei.Feng@Sun.COM
72810128SFei.Feng@Sun.COM p = (char *)ddi_modsym(modp, start_sym, &rv);
72910128SFei.Feng@Sun.COM if (p == NULL || rv != 0) {
73010128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_loadsym(): "
73110128SFei.Feng@Sun.COM "mod %s: symbol %s not found\n", uath_fwmod, start_sym);
73210128SFei.Feng@Sun.COM return (UATH_FAILURE);
73310128SFei.Feng@Sun.COM }
73410128SFei.Feng@Sun.COM
73510128SFei.Feng@Sun.COM end = (char *)ddi_modsym(modp, end_sym, &rv);
73610128SFei.Feng@Sun.COM if (end == NULL || rv != 0) {
73710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_loadsym(): "
73810128SFei.Feng@Sun.COM "mod %s: symbol %s not found\n", uath_fwmod, end_sym);
73910128SFei.Feng@Sun.COM return (UATH_FAILURE);
74010128SFei.Feng@Sun.COM }
74110128SFei.Feng@Sun.COM
74210128SFei.Feng@Sun.COM n = _PTRDIFF(end, p);
74310128SFei.Feng@Sun.COM *start = p;
74410128SFei.Feng@Sun.COM *len = n;
74510128SFei.Feng@Sun.COM
74610128SFei.Feng@Sun.COM return (UATH_SUCCESS);
74710128SFei.Feng@Sun.COM }
74810128SFei.Feng@Sun.COM
74910128SFei.Feng@Sun.COM /*
75010128SFei.Feng@Sun.COM * Load the MIPS R4000 microcode into the device. Once the image is loaded,
75110128SFei.Feng@Sun.COM * the device will detach itself from the bus and reattach later with a new
75210128SFei.Feng@Sun.COM * product Id (a la ezusb). XXX this could also be implemented in userland
75310128SFei.Feng@Sun.COM * through /dev/ugen.
75410128SFei.Feng@Sun.COM */
75510128SFei.Feng@Sun.COM static int
uath_loadfirmware(struct uath_softc * sc)75610128SFei.Feng@Sun.COM uath_loadfirmware(struct uath_softc *sc)
75710128SFei.Feng@Sun.COM {
75810128SFei.Feng@Sun.COM struct uath_fwblock txblock;
75910128SFei.Feng@Sun.COM ddi_modhandle_t modp;
76010128SFei.Feng@Sun.COM char *fw_index, *fw_image = NULL;
76110128SFei.Feng@Sun.COM size_t fw_size, len;
76210128SFei.Feng@Sun.COM int err = DDI_SUCCESS, rv = 0;
76310128SFei.Feng@Sun.COM
76410128SFei.Feng@Sun.COM modp = ddi_modopen(uath_fwmod, KRTLD_MODE_FIRST, &rv);
76510128SFei.Feng@Sun.COM if (modp == NULL) {
76610128SFei.Feng@Sun.COM cmn_err(CE_WARN, "uath: uath_loadfirmware(): "
76710128SFei.Feng@Sun.COM "module %s not found\n", uath_fwmod);
76810128SFei.Feng@Sun.COM goto label;
76910128SFei.Feng@Sun.COM }
77010128SFei.Feng@Sun.COM
77110128SFei.Feng@Sun.COM err = uath_loadsym(modp, uath_binmod, &fw_index, &fw_size);
77210128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
77310128SFei.Feng@Sun.COM cmn_err(CE_WARN, "uath: uath_loadfirmware(): "
77410128SFei.Feng@Sun.COM "could not get firmware\n");
77510128SFei.Feng@Sun.COM goto label;
77610128SFei.Feng@Sun.COM }
77710128SFei.Feng@Sun.COM
77810128SFei.Feng@Sun.COM fw_image = (char *)kmem_alloc(fw_size, KM_SLEEP);
77910128SFei.Feng@Sun.COM if (fw_image == NULL) {
78010128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_loadfirmware(): "
78110128SFei.Feng@Sun.COM "failed to alloc firmware memory\n");
78210128SFei.Feng@Sun.COM err = UATH_FAILURE;
78310128SFei.Feng@Sun.COM goto label;
78410128SFei.Feng@Sun.COM }
78510128SFei.Feng@Sun.COM
78610128SFei.Feng@Sun.COM (void) memcpy(fw_image, fw_index, fw_size);
78710128SFei.Feng@Sun.COM fw_index = fw_image;
78810128SFei.Feng@Sun.COM len = fw_size;
78910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "loading firmware size = %lu\n", fw_size);
79010128SFei.Feng@Sun.COM
79110128SFei.Feng@Sun.COM /* bzero(txblock, sizeof (struct uath_fwblock)); */
79210128SFei.Feng@Sun.COM txblock.flags = BE_32(UATH_WRITE_BLOCK);
79310128SFei.Feng@Sun.COM txblock.total = BE_32(fw_size);
79410128SFei.Feng@Sun.COM
79510128SFei.Feng@Sun.COM while (len > 0) {
79610128SFei.Feng@Sun.COM size_t mlen = min(len, UATH_MAX_FWBLOCK_SIZE);
79710128SFei.Feng@Sun.COM
79810128SFei.Feng@Sun.COM txblock.remain = BE_32(len - mlen);
79910128SFei.Feng@Sun.COM txblock.len = BE_32(mlen);
80010128SFei.Feng@Sun.COM
80110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_FW, "uath: uath_loadfirmware(): "
80210128SFei.Feng@Sun.COM "sending firmware block: %d bytes sending\n", mlen);
80310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_FW, "uath: uath_loadfirmware(): "
80410128SFei.Feng@Sun.COM "sending firmware block: %d bytes remaining\n",
80510128SFei.Feng@Sun.COM len - mlen);
80610128SFei.Feng@Sun.COM
80710128SFei.Feng@Sun.COM /* send firmware block meta-data */
80810128SFei.Feng@Sun.COM err = uath_fw_send(sc, sc->tx_cmd_pipe, &txblock,
80910128SFei.Feng@Sun.COM sizeof (struct uath_fwblock));
81010128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
81110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_FW, "uath: uath_loadfirmware(): "
81210128SFei.Feng@Sun.COM "send block meta-data error");
81310128SFei.Feng@Sun.COM goto label;
81410128SFei.Feng@Sun.COM }
81510128SFei.Feng@Sun.COM
81610128SFei.Feng@Sun.COM /* send firmware block data */
81710128SFei.Feng@Sun.COM err = uath_fw_send(sc, sc->tx_data_pipe, fw_index, mlen);
81810128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
81910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_FW, "uath: uath_loadfirmware() "
82010128SFei.Feng@Sun.COM "send block data err");
82110128SFei.Feng@Sun.COM goto label;
82210128SFei.Feng@Sun.COM }
82310128SFei.Feng@Sun.COM
82410128SFei.Feng@Sun.COM /* wait for ack from firmware */
82510128SFei.Feng@Sun.COM err = uath_fw_ack(sc, sizeof (struct uath_fwblock));
82610128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
82710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_FW, "uath: uath_loadfirmware() "
82810128SFei.Feng@Sun.COM "rx block ack err");
82910128SFei.Feng@Sun.COM goto label;
83010128SFei.Feng@Sun.COM }
83110128SFei.Feng@Sun.COM
83210128SFei.Feng@Sun.COM fw_index += mlen;
83310128SFei.Feng@Sun.COM len -= mlen;
83410128SFei.Feng@Sun.COM }
83510128SFei.Feng@Sun.COM
83610128SFei.Feng@Sun.COM label:
83710128SFei.Feng@Sun.COM if (fw_image != NULL)
83810128SFei.Feng@Sun.COM kmem_free(fw_image, fw_size);
83910128SFei.Feng@Sun.COM fw_image = fw_index = NULL;
84010128SFei.Feng@Sun.COM if (modp != NULL)
84110128SFei.Feng@Sun.COM (void) ddi_modclose(modp);
84210128SFei.Feng@Sun.COM return (err);
84310128SFei.Feng@Sun.COM }
84410128SFei.Feng@Sun.COM
84510128SFei.Feng@Sun.COM static int
uath_alloc_cmd_list(struct uath_softc * sc,struct uath_cmd cmds[],int ncmd,int maxsz)84610128SFei.Feng@Sun.COM uath_alloc_cmd_list(struct uath_softc *sc, struct uath_cmd cmds[],
84710128SFei.Feng@Sun.COM int ncmd, int maxsz)
84810128SFei.Feng@Sun.COM {
84910128SFei.Feng@Sun.COM int i, err;
85010128SFei.Feng@Sun.COM
85110128SFei.Feng@Sun.COM for (i = 0; i < ncmd; i++) {
85210128SFei.Feng@Sun.COM struct uath_cmd *cmd = &cmds[i];
85310128SFei.Feng@Sun.COM
85410128SFei.Feng@Sun.COM cmd->sc = sc; /* backpointer for callbacks */
85510128SFei.Feng@Sun.COM cmd->msgid = i;
85610128SFei.Feng@Sun.COM cmd->buf = kmem_zalloc(maxsz, KM_NOSLEEP);
85710128SFei.Feng@Sun.COM if (cmd->buf == NULL) {
85810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_alloc_cmd_list(): "
85910128SFei.Feng@Sun.COM "could not allocate xfer buffer\n");
86010128SFei.Feng@Sun.COM err = DDI_ENOMEM;
86110128SFei.Feng@Sun.COM goto fail;
86210128SFei.Feng@Sun.COM }
86310128SFei.Feng@Sun.COM }
86410128SFei.Feng@Sun.COM return (UATH_SUCCESS);
86510128SFei.Feng@Sun.COM
86610128SFei.Feng@Sun.COM fail:
86710128SFei.Feng@Sun.COM uath_free_cmd_list(cmds, ncmd);
86810128SFei.Feng@Sun.COM return (err);
86910128SFei.Feng@Sun.COM }
87010128SFei.Feng@Sun.COM
87110128SFei.Feng@Sun.COM static int
uath_init_cmd_list(struct uath_softc * sc)87210128SFei.Feng@Sun.COM uath_init_cmd_list(struct uath_softc *sc)
87310128SFei.Feng@Sun.COM {
87410128SFei.Feng@Sun.COM int i;
87510128SFei.Feng@Sun.COM
87610128SFei.Feng@Sun.COM sc->sc_cmdid = sc->rx_cmd_queued = sc->tx_cmd_queued = 0;
87710128SFei.Feng@Sun.COM for (i = 0; i < UATH_CMD_LIST_COUNT; i++) {
87810128SFei.Feng@Sun.COM if (uath_rx_cmd_xfer(sc) != UATH_SUCCESS) {
87910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_init_cmd_list(): "
88010128SFei.Feng@Sun.COM "failed to init cmd list %x\n", i);
88110128SFei.Feng@Sun.COM return (UATH_FAILURE);
88210128SFei.Feng@Sun.COM }
88310128SFei.Feng@Sun.COM }
88410128SFei.Feng@Sun.COM return (UATH_SUCCESS);
88510128SFei.Feng@Sun.COM }
88610128SFei.Feng@Sun.COM
88710128SFei.Feng@Sun.COM static void
uath_free_cmd_list(struct uath_cmd cmds[],int ncmd)88810128SFei.Feng@Sun.COM uath_free_cmd_list(struct uath_cmd cmds[], int ncmd)
88910128SFei.Feng@Sun.COM {
89010128SFei.Feng@Sun.COM int i;
89110128SFei.Feng@Sun.COM
89210128SFei.Feng@Sun.COM for (i = 0; i < ncmd; i++)
89310128SFei.Feng@Sun.COM if (cmds[i].buf != NULL) {
89410128SFei.Feng@Sun.COM kmem_free(cmds[i].buf, UATH_MAX_CMDSZ);
89510128SFei.Feng@Sun.COM cmds[i].buf = NULL;
89610128SFei.Feng@Sun.COM }
89710128SFei.Feng@Sun.COM }
89810128SFei.Feng@Sun.COM
89910128SFei.Feng@Sun.COM static int
uath_host_available(struct uath_softc * sc)90010128SFei.Feng@Sun.COM uath_host_available(struct uath_softc *sc)
90110128SFei.Feng@Sun.COM {
90210128SFei.Feng@Sun.COM struct uath_cmd_host_available setup;
90310128SFei.Feng@Sun.COM
90410128SFei.Feng@Sun.COM /* inform target the host is available */
90510128SFei.Feng@Sun.COM setup.sw_ver_major = BE_32(ATH_SW_VER_MAJOR);
90610128SFei.Feng@Sun.COM setup.sw_ver_minor = BE_32(ATH_SW_VER_MINOR);
90710128SFei.Feng@Sun.COM setup.sw_ver_patch = BE_32(ATH_SW_VER_PATCH);
90810128SFei.Feng@Sun.COM setup.sw_ver_build = BE_32(ATH_SW_VER_BUILD);
90910128SFei.Feng@Sun.COM return (uath_cmd_read(sc, WDCMSG_HOST_AVAILABLE,
91010128SFei.Feng@Sun.COM &setup, sizeof (setup), NULL, 0, 0));
91110128SFei.Feng@Sun.COM }
91210128SFei.Feng@Sun.COM
91310128SFei.Feng@Sun.COM static void
uath_get_capability(struct uath_softc * sc,uint32_t cap,uint32_t * val)91410128SFei.Feng@Sun.COM uath_get_capability(struct uath_softc *sc, uint32_t cap, uint32_t *val)
91510128SFei.Feng@Sun.COM {
91610128SFei.Feng@Sun.COM int err;
91710128SFei.Feng@Sun.COM
91810128SFei.Feng@Sun.COM cap = BE_32(cap);
91910128SFei.Feng@Sun.COM err = uath_cmd_read(sc, WDCMSG_TARGET_GET_CAPABILITY, &cap,
92010128SFei.Feng@Sun.COM sizeof (cap), val, sizeof (uint32_t), UATH_CMD_FLAG_MAGIC);
92110128SFei.Feng@Sun.COM if (err == UATH_SUCCESS)
92210128SFei.Feng@Sun.COM *val = BE_32(*val);
92310128SFei.Feng@Sun.COM else
92410128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_get_capability(): "
92510128SFei.Feng@Sun.COM "could not read capability %u\n", BE_32(cap));
92610128SFei.Feng@Sun.COM }
92710128SFei.Feng@Sun.COM
92810128SFei.Feng@Sun.COM static int
uath_get_devcap(struct uath_softc * sc)92910128SFei.Feng@Sun.COM uath_get_devcap(struct uath_softc *sc)
93010128SFei.Feng@Sun.COM {
93110128SFei.Feng@Sun.COM struct uath_devcap *cap = &sc->sc_devcap;
93210128SFei.Feng@Sun.COM
93310128SFei.Feng@Sun.COM /* collect device capabilities */
93410128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_TARGET_VERSION,
93510128SFei.Feng@Sun.COM &cap->targetVersion);
93610128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_TARGET_REVISION,
93710128SFei.Feng@Sun.COM &cap->targetRevision);
93810128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_MAC_VERSION,
93910128SFei.Feng@Sun.COM &cap->macVersion);
94010128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_MAC_REVISION,
94110128SFei.Feng@Sun.COM &cap->macRevision);
94210128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_PHY_REVISION,
94310128SFei.Feng@Sun.COM &cap->phyRevision);
94410128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_ANALOG_5GHz_REVISION,
94510128SFei.Feng@Sun.COM &cap->analog5GhzRevision);
94610128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_ANALOG_2GHz_REVISION,
94710128SFei.Feng@Sun.COM &cap->analog2GhzRevision);
94810128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_REG_DOMAIN,
94910128SFei.Feng@Sun.COM &cap->regDomain);
95010128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_REG_CAP_BITS,
95110128SFei.Feng@Sun.COM &cap->regCapBits);
95210128SFei.Feng@Sun.COM
95310128SFei.Feng@Sun.COM /* NB: not supported in rev 1.5 */
95410128SFei.Feng@Sun.COM /* uath_get_capability(sc, CAP_COUNTRY_CODE, cap->countryCode); */
95510128SFei.Feng@Sun.COM
95610128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_WIRELESS_MODES,
95710128SFei.Feng@Sun.COM &cap->wirelessModes);
95810128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_CHAN_SPREAD_SUPPORT,
95910128SFei.Feng@Sun.COM &cap->chanSpreadSupport);
96010128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_COMPRESS_SUPPORT,
96110128SFei.Feng@Sun.COM &cap->compressSupport);
96210128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_BURST_SUPPORT,
96310128SFei.Feng@Sun.COM &cap->burstSupport);
96410128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_FAST_FRAMES_SUPPORT,
96510128SFei.Feng@Sun.COM &cap->fastFramesSupport);
96610128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_CHAP_TUNING_SUPPORT,
96710128SFei.Feng@Sun.COM &cap->chapTuningSupport);
96810128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_TURBOG_SUPPORT,
96910128SFei.Feng@Sun.COM &cap->turboGSupport);
97010128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_TURBO_PRIME_SUPPORT,
97110128SFei.Feng@Sun.COM &cap->turboPrimeSupport);
97210128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_DEVICE_TYPE,
97310128SFei.Feng@Sun.COM &cap->deviceType);
97410128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_WME_SUPPORT,
97510128SFei.Feng@Sun.COM &cap->wmeSupport);
97610128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_TOTAL_QUEUES,
97710128SFei.Feng@Sun.COM &cap->numTxQueues);
97810128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_CONNECTION_ID_MAX,
97910128SFei.Feng@Sun.COM &cap->connectionIdMax);
98010128SFei.Feng@Sun.COM
98110128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_LOW_5GHZ_CHAN,
98210128SFei.Feng@Sun.COM &cap->low5GhzChan);
98310128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_HIGH_5GHZ_CHAN,
98410128SFei.Feng@Sun.COM &cap->high5GhzChan);
98510128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_LOW_2GHZ_CHAN,
98610128SFei.Feng@Sun.COM &cap->low2GhzChan);
98710128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_HIGH_2GHZ_CHAN,
98810128SFei.Feng@Sun.COM &cap->high2GhzChan);
98910128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_TWICE_ANTENNAGAIN_5G,
99010128SFei.Feng@Sun.COM &cap->twiceAntennaGain5G);
99110128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_TWICE_ANTENNAGAIN_2G,
99210128SFei.Feng@Sun.COM &cap->twiceAntennaGain2G);
99310128SFei.Feng@Sun.COM
99410128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_CIPHER_AES_CCM,
99510128SFei.Feng@Sun.COM &cap->supportCipherAES_CCM);
99610128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_CIPHER_TKIP,
99710128SFei.Feng@Sun.COM &cap->supportCipherTKIP);
99810128SFei.Feng@Sun.COM uath_get_capability(sc, CAP_MIC_TKIP,
99910128SFei.Feng@Sun.COM &cap->supportMicTKIP);
100010128SFei.Feng@Sun.COM
100110128SFei.Feng@Sun.COM cap->supportCipherWEP = 1; /* NB: always available */
100210128SFei.Feng@Sun.COM return (UATH_SUCCESS);
100310128SFei.Feng@Sun.COM }
100410128SFei.Feng@Sun.COM
100510128SFei.Feng@Sun.COM static int
uath_get_status(struct uath_softc * sc,uint32_t which,void * odata,int olen)100610128SFei.Feng@Sun.COM uath_get_status(struct uath_softc *sc, uint32_t which, void *odata, int olen)
100710128SFei.Feng@Sun.COM {
100810128SFei.Feng@Sun.COM int err;
100910128SFei.Feng@Sun.COM
101010128SFei.Feng@Sun.COM which = BE_32(which);
101110128SFei.Feng@Sun.COM err = uath_cmd_read(sc, WDCMSG_TARGET_GET_STATUS,
101210128SFei.Feng@Sun.COM &which, sizeof (which), odata, olen, UATH_CMD_FLAG_MAGIC);
101310128SFei.Feng@Sun.COM if (err != UATH_SUCCESS)
101410128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_get_status(): "
101510128SFei.Feng@Sun.COM "could not read EEPROM offset 0x%02x\n", BE_32(which));
101610128SFei.Feng@Sun.COM return (err);
101710128SFei.Feng@Sun.COM }
101810128SFei.Feng@Sun.COM
101910128SFei.Feng@Sun.COM static int
uath_get_devstatus(struct uath_softc * sc,uint8_t macaddr[IEEE80211_ADDR_LEN])102010128SFei.Feng@Sun.COM uath_get_devstatus(struct uath_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN])
102110128SFei.Feng@Sun.COM {
102210128SFei.Feng@Sun.COM int err;
102310128SFei.Feng@Sun.COM
102410128SFei.Feng@Sun.COM /* retrieve MAC address */
102510128SFei.Feng@Sun.COM err = uath_get_status(sc, ST_MAC_ADDR, macaddr, IEEE80211_ADDR_LEN);
102610128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
102710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_get_devstatus(): "
102810128SFei.Feng@Sun.COM "could not read MAC address\n");
102910128SFei.Feng@Sun.COM return (err);
103010128SFei.Feng@Sun.COM }
103110128SFei.Feng@Sun.COM
103210128SFei.Feng@Sun.COM err = uath_get_status(sc, ST_SERIAL_NUMBER,
103310128SFei.Feng@Sun.COM &sc->sc_serial[0], sizeof (sc->sc_serial));
103410128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
103510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_get_devstatus(): "
103610128SFei.Feng@Sun.COM "could not read device serial number\n");
103710128SFei.Feng@Sun.COM return (err);
103810128SFei.Feng@Sun.COM }
103910128SFei.Feng@Sun.COM
104010128SFei.Feng@Sun.COM return (UATH_SUCCESS);
104110128SFei.Feng@Sun.COM }
104210128SFei.Feng@Sun.COM
104310128SFei.Feng@Sun.COM /*
104410128SFei.Feng@Sun.COM * uath_cmd_lock: a special signal structure that is used for notification
104510128SFei.Feng@Sun.COM * that a callback function has been called.
104610128SFei.Feng@Sun.COM */
104710128SFei.Feng@Sun.COM
104810128SFei.Feng@Sun.COM /* Initializes the uath_cmd_lock structure. */
104910128SFei.Feng@Sun.COM static void
uath_cmd_lock_init(struct uath_cmd_lock * lock)105010128SFei.Feng@Sun.COM uath_cmd_lock_init(struct uath_cmd_lock *lock)
105110128SFei.Feng@Sun.COM {
105210128SFei.Feng@Sun.COM ASSERT(lock != NULL);
105310128SFei.Feng@Sun.COM mutex_init(&lock->mutex, NULL, MUTEX_DRIVER, NULL);
105410128SFei.Feng@Sun.COM cv_init(&lock->cv, NULL, CV_DRIVER, NULL);
105510128SFei.Feng@Sun.COM lock->done = B_FALSE;
105610128SFei.Feng@Sun.COM }
105710128SFei.Feng@Sun.COM
105810128SFei.Feng@Sun.COM /* Deinitalizes the uath_cb_lock structure. */
105910128SFei.Feng@Sun.COM void
uath_cmd_lock_destroy(struct uath_cmd_lock * lock)106010128SFei.Feng@Sun.COM uath_cmd_lock_destroy(struct uath_cmd_lock *lock)
106110128SFei.Feng@Sun.COM {
106210128SFei.Feng@Sun.COM ASSERT(lock != NULL);
106310128SFei.Feng@Sun.COM mutex_destroy(&lock->mutex);
106410128SFei.Feng@Sun.COM cv_destroy(&lock->cv);
106510128SFei.Feng@Sun.COM }
106610128SFei.Feng@Sun.COM
106710128SFei.Feng@Sun.COM /*
106810128SFei.Feng@Sun.COM * Wait on lock until someone calls the "signal" function or the timeout
106910128SFei.Feng@Sun.COM * expires. Note: timeout is in microseconds.
107010128SFei.Feng@Sun.COM */
107110128SFei.Feng@Sun.COM static int
uath_cmd_lock_wait(struct uath_cmd_lock * lock,clock_t timeout)107210128SFei.Feng@Sun.COM uath_cmd_lock_wait(struct uath_cmd_lock *lock, clock_t timeout)
107310128SFei.Feng@Sun.COM {
107410128SFei.Feng@Sun.COM int res, cv_res;
107510128SFei.Feng@Sun.COM clock_t etime;
107610128SFei.Feng@Sun.COM
107710128SFei.Feng@Sun.COM ASSERT(lock != NULL);
107810128SFei.Feng@Sun.COM mutex_enter(&lock->mutex);
107910128SFei.Feng@Sun.COM
108010128SFei.Feng@Sun.COM if (timeout < 0) {
108110128SFei.Feng@Sun.COM /* no timeout - wait as long as needed */
108210128SFei.Feng@Sun.COM while (lock->done == B_FALSE)
108310128SFei.Feng@Sun.COM cv_wait(&lock->cv, &lock->mutex);
108410128SFei.Feng@Sun.COM } else {
108510128SFei.Feng@Sun.COM /* wait with timeout (given in usec) */
108610128SFei.Feng@Sun.COM etime = ddi_get_lbolt() + drv_usectohz(timeout);
108710128SFei.Feng@Sun.COM while (lock->done == B_FALSE) {
108810128SFei.Feng@Sun.COM cv_res = cv_timedwait_sig(&lock->cv,
108910128SFei.Feng@Sun.COM &lock->mutex, etime);
109010128SFei.Feng@Sun.COM if (cv_res <= 0) break;
109110128SFei.Feng@Sun.COM }
109210128SFei.Feng@Sun.COM }
109310128SFei.Feng@Sun.COM
109410128SFei.Feng@Sun.COM res = (lock->done == B_TRUE) ? UATH_SUCCESS : UATH_FAILURE;
109510128SFei.Feng@Sun.COM mutex_exit(&lock->mutex);
109610128SFei.Feng@Sun.COM
109710128SFei.Feng@Sun.COM return (res);
109810128SFei.Feng@Sun.COM }
109910128SFei.Feng@Sun.COM
110010128SFei.Feng@Sun.COM /* Signal that the job (eg. callback) is done and unblock anyone who waits. */
110110128SFei.Feng@Sun.COM static void
uath_cmd_lock_signal(struct uath_cmd_lock * lock)110210128SFei.Feng@Sun.COM uath_cmd_lock_signal(struct uath_cmd_lock *lock)
110310128SFei.Feng@Sun.COM {
110410128SFei.Feng@Sun.COM ASSERT(lock != NULL);
110510128SFei.Feng@Sun.COM
110610128SFei.Feng@Sun.COM mutex_enter(&lock->mutex);
110710128SFei.Feng@Sun.COM lock->done = B_TRUE;
110810128SFei.Feng@Sun.COM cv_broadcast(&lock->cv);
110910128SFei.Feng@Sun.COM mutex_exit(&lock->mutex);
111010128SFei.Feng@Sun.COM }
111110128SFei.Feng@Sun.COM
111210128SFei.Feng@Sun.COM static int
uath_cmd_read(struct uath_softc * sc,uint32_t code,const void * idata,int ilen,void * odata,int olen,int flags)111310128SFei.Feng@Sun.COM uath_cmd_read(struct uath_softc *sc, uint32_t code, const void *idata,
111410128SFei.Feng@Sun.COM int ilen, void *odata, int olen, int flags)
111510128SFei.Feng@Sun.COM {
111610128SFei.Feng@Sun.COM flags |= UATH_CMD_FLAG_READ;
111710128SFei.Feng@Sun.COM return (uath_cmdsend(sc, code, idata, ilen, odata, olen, flags));
111810128SFei.Feng@Sun.COM }
111910128SFei.Feng@Sun.COM
112010128SFei.Feng@Sun.COM static int
uath_cmd_write(struct uath_softc * sc,uint32_t code,const void * data,int len,int flags)112110128SFei.Feng@Sun.COM uath_cmd_write(struct uath_softc *sc, uint32_t code, const void *data,
112210128SFei.Feng@Sun.COM int len, int flags)
112310128SFei.Feng@Sun.COM {
112410128SFei.Feng@Sun.COM flags &= ~UATH_CMD_FLAG_READ;
112510128SFei.Feng@Sun.COM return (uath_cmdsend(sc, code, data, len, NULL, 0, flags));
112610128SFei.Feng@Sun.COM }
112710128SFei.Feng@Sun.COM
112810128SFei.Feng@Sun.COM /*
112910128SFei.Feng@Sun.COM * Low-level function to send read or write commands to the firmware.
113010128SFei.Feng@Sun.COM */
113110128SFei.Feng@Sun.COM static int
uath_cmdsend(struct uath_softc * sc,uint32_t code,const void * idata,int ilen,void * odata,int olen,int flags)113210128SFei.Feng@Sun.COM uath_cmdsend(struct uath_softc *sc, uint32_t code, const void *idata, int ilen,
113310128SFei.Feng@Sun.COM void *odata, int olen, int flags)
113410128SFei.Feng@Sun.COM {
113510128SFei.Feng@Sun.COM struct uath_cmd_hdr *hdr;
113610128SFei.Feng@Sun.COM struct uath_cmd *cmd;
113710128SFei.Feng@Sun.COM int err;
113810128SFei.Feng@Sun.COM
113910128SFei.Feng@Sun.COM /* grab a xfer */
114010128SFei.Feng@Sun.COM cmd = &sc->sc_cmd[sc->sc_cmdid];
114110128SFei.Feng@Sun.COM
114210128SFei.Feng@Sun.COM cmd->flags = flags;
114310128SFei.Feng@Sun.COM /* always bulk-out a multiple of 4 bytes */
114410128SFei.Feng@Sun.COM cmd->buflen = (sizeof (struct uath_cmd_hdr) + ilen + 3) & ~3;
114510128SFei.Feng@Sun.COM
114610128SFei.Feng@Sun.COM hdr = (struct uath_cmd_hdr *)cmd->buf;
114710128SFei.Feng@Sun.COM bzero(hdr, sizeof (struct uath_cmd_hdr));
114810128SFei.Feng@Sun.COM hdr->len = BE_32(cmd->buflen);
114910128SFei.Feng@Sun.COM hdr->code = BE_32(code);
115010128SFei.Feng@Sun.COM hdr->msgid = cmd->msgid; /* don't care about endianness */
115110128SFei.Feng@Sun.COM hdr->magic = BE_32((cmd->flags & UATH_CMD_FLAG_MAGIC) ? 1 << 24 : 0);
115210128SFei.Feng@Sun.COM bcopy(idata, (uint8_t *)(hdr + 1), ilen);
115310128SFei.Feng@Sun.COM
115410128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_TX_CMD, "uath: uath_cmdsend(): "
115510128SFei.Feng@Sun.COM "queue %x send %s [flags 0x%x] olen %d\n",
115610128SFei.Feng@Sun.COM cmd->msgid, uath_codename(code), cmd->flags, olen);
115710128SFei.Feng@Sun.COM
115810128SFei.Feng@Sun.COM cmd->odata = odata;
115910128SFei.Feng@Sun.COM if (odata == NULL)
116010128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_TX_CMD, "uath: uath_cmdsend(): "
116110128SFei.Feng@Sun.COM "warning - odata is NULL\n");
116210128SFei.Feng@Sun.COM else if (olen < UATH_MAX_CMDSZ - sizeof (*hdr) + sizeof (uint32_t))
116310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_TX_CMD, "uath: uath_cmdsend(): "
116410128SFei.Feng@Sun.COM "warning - olen %x is short\n, olen");
116510128SFei.Feng@Sun.COM cmd->olen = olen;
116610128SFei.Feng@Sun.COM
116710128SFei.Feng@Sun.COM err = uath_tx_cmd_xfer(sc, sc->tx_cmd_pipe, cmd->buf, cmd->buflen);
116810128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
116910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_cmdsend(): "
117010128SFei.Feng@Sun.COM "Error writing command\n");
117110128SFei.Feng@Sun.COM return (UATH_FAILURE);
117210128SFei.Feng@Sun.COM }
117310128SFei.Feng@Sun.COM
117410128SFei.Feng@Sun.COM sc->sc_cmdid = (sc->sc_cmdid + 1) % UATH_CMD_LIST_COUNT;
117510128SFei.Feng@Sun.COM
117610128SFei.Feng@Sun.COM if (cmd->flags & UATH_CMD_FLAG_READ) {
117710128SFei.Feng@Sun.COM /* wait at most two seconds for command reply */
117810128SFei.Feng@Sun.COM uath_cmd_lock_init(&sc->rlock);
117910128SFei.Feng@Sun.COM err = uath_cmd_lock_wait(&sc->rlock, 2000000);
118010128SFei.Feng@Sun.COM cmd->odata = NULL; /* in case reply comes too late */
118110128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
118210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_cmdsend(): "
118310128SFei.Feng@Sun.COM "timeout waiting for reply, "
118410128SFei.Feng@Sun.COM "to cmd 0x%x (%u), queue %x\n",
118510128SFei.Feng@Sun.COM code, code, cmd->msgid);
118610128SFei.Feng@Sun.COM err = UATH_FAILURE;
118710128SFei.Feng@Sun.COM } else if (cmd->olen != olen) {
118810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_cmdsend(): "
118910128SFei.Feng@Sun.COM "unexpected reply data count "
119010128SFei.Feng@Sun.COM "to cmd 0x%x (%x), got %u, expected %u\n",
119110128SFei.Feng@Sun.COM code, cmd->msgid, cmd->olen, olen);
119210128SFei.Feng@Sun.COM err = UATH_FAILURE;
119310128SFei.Feng@Sun.COM }
119410128SFei.Feng@Sun.COM uath_cmd_lock_destroy(&sc->rlock);
119510128SFei.Feng@Sun.COM return (err);
119610128SFei.Feng@Sun.COM }
119710128SFei.Feng@Sun.COM
119810128SFei.Feng@Sun.COM return (UATH_SUCCESS);
119910128SFei.Feng@Sun.COM }
120010128SFei.Feng@Sun.COM
120110128SFei.Feng@Sun.COM /* ARGSUSED */
120210128SFei.Feng@Sun.COM static void
uath_cmd_txeof(usb_pipe_handle_t pipe,struct usb_bulk_req * req)120310128SFei.Feng@Sun.COM uath_cmd_txeof(usb_pipe_handle_t pipe, struct usb_bulk_req *req)
120410128SFei.Feng@Sun.COM {
120510128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)req->bulk_client_private;
120610128SFei.Feng@Sun.COM
120710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_TX_CMD, "uath: uath_cmd_txeof(): "
120810128SFei.Feng@Sun.COM "cr:%s(%d), flags:0x%x, tx queued %d\n",
120910128SFei.Feng@Sun.COM usb_str_cr(req->bulk_completion_reason),
121010128SFei.Feng@Sun.COM req->bulk_completion_reason,
121110128SFei.Feng@Sun.COM req->bulk_cb_flags,
121210128SFei.Feng@Sun.COM sc->tx_cmd_queued);
121310128SFei.Feng@Sun.COM
121410128SFei.Feng@Sun.COM if (req->bulk_completion_reason != USB_CR_OK)
121510128SFei.Feng@Sun.COM sc->sc_tx_err++;
121610128SFei.Feng@Sun.COM
121710128SFei.Feng@Sun.COM mutex_enter(&sc->sc_txlock_cmd);
121810128SFei.Feng@Sun.COM sc->tx_cmd_queued--;
121910128SFei.Feng@Sun.COM mutex_exit(&sc->sc_txlock_cmd);
122010128SFei.Feng@Sun.COM usb_free_bulk_req(req);
122110128SFei.Feng@Sun.COM }
122210128SFei.Feng@Sun.COM
122310128SFei.Feng@Sun.COM static int
uath_tx_cmd_xfer(struct uath_softc * sc,usb_pipe_handle_t pipe,const void * data,uint_t len)122410128SFei.Feng@Sun.COM uath_tx_cmd_xfer(struct uath_softc *sc,
122510128SFei.Feng@Sun.COM usb_pipe_handle_t pipe, const void *data, uint_t len)
122610128SFei.Feng@Sun.COM {
122710128SFei.Feng@Sun.COM usb_bulk_req_t *send_req;
122810128SFei.Feng@Sun.COM mblk_t *mblk;
122910128SFei.Feng@Sun.COM int res;
123010128SFei.Feng@Sun.COM
123110128SFei.Feng@Sun.COM send_req = usb_alloc_bulk_req(sc->sc_dev, len, USB_FLAGS_SLEEP);
123210128SFei.Feng@Sun.COM
123310128SFei.Feng@Sun.COM send_req->bulk_client_private = (usb_opaque_t)sc;
123410128SFei.Feng@Sun.COM send_req->bulk_len = (int)len;
123510128SFei.Feng@Sun.COM send_req->bulk_attributes = USB_ATTRS_AUTOCLEARING;
123610128SFei.Feng@Sun.COM send_req->bulk_timeout = UATH_CMD_TIMEOUT;
123710128SFei.Feng@Sun.COM send_req->bulk_cb = uath_cmd_txeof;
123810128SFei.Feng@Sun.COM send_req->bulk_exc_cb = uath_cmd_txeof;
123910128SFei.Feng@Sun.COM send_req->bulk_completion_reason = 0;
124010128SFei.Feng@Sun.COM send_req->bulk_cb_flags = 0;
124110128SFei.Feng@Sun.COM
124210128SFei.Feng@Sun.COM mblk = send_req->bulk_data;
124310128SFei.Feng@Sun.COM bcopy(data, mblk->b_rptr, len);
124410128SFei.Feng@Sun.COM mblk->b_wptr += len;
124510128SFei.Feng@Sun.COM
124610128SFei.Feng@Sun.COM res = usb_pipe_bulk_xfer(pipe, send_req, USB_FLAGS_NOSLEEP);
124710128SFei.Feng@Sun.COM if (res != UATH_SUCCESS) {
124810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_tx_cmd_xfer(): "
124910128SFei.Feng@Sun.COM "Error %x writing cmd to bulk/out pipe", res);
125010128SFei.Feng@Sun.COM return (UATH_FAILURE);
125110128SFei.Feng@Sun.COM }
125210128SFei.Feng@Sun.COM
125310128SFei.Feng@Sun.COM mutex_enter(&sc->sc_txlock_cmd);
125410128SFei.Feng@Sun.COM sc->tx_cmd_queued++;
125510128SFei.Feng@Sun.COM mutex_exit(&sc->sc_txlock_cmd);
125610128SFei.Feng@Sun.COM return (UATH_SUCCESS);
125710128SFei.Feng@Sun.COM }
125810128SFei.Feng@Sun.COM
125910128SFei.Feng@Sun.COM static void
uath_cmdeof(struct uath_softc * sc,struct uath_cmd * cmd)126010128SFei.Feng@Sun.COM uath_cmdeof(struct uath_softc *sc, struct uath_cmd *cmd)
126110128SFei.Feng@Sun.COM {
126210128SFei.Feng@Sun.COM struct uath_cmd_hdr *hdr;
126310128SFei.Feng@Sun.COM int dlen;
126410128SFei.Feng@Sun.COM
126510128SFei.Feng@Sun.COM hdr = (struct uath_cmd_hdr *)cmd->buf;
126610128SFei.Feng@Sun.COM
126710128SFei.Feng@Sun.COM hdr->code = BE_32(hdr->code);
126810128SFei.Feng@Sun.COM hdr->len = BE_32(hdr->len);
126910128SFei.Feng@Sun.COM hdr->magic = BE_32(hdr->magic); /* target status on return */
127010128SFei.Feng@Sun.COM
127110128SFei.Feng@Sun.COM /* NB: msgid is passed thru w/o byte swapping */
127210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX_CMD, "uath: uath_cmdeof(): "
127310128SFei.Feng@Sun.COM "%s: [ix %x] len=%x status %x\n",
127410128SFei.Feng@Sun.COM uath_codename(hdr->code),
127510128SFei.Feng@Sun.COM hdr->msgid,
127610128SFei.Feng@Sun.COM hdr->len,
127710128SFei.Feng@Sun.COM hdr->magic);
127810128SFei.Feng@Sun.COM
127910128SFei.Feng@Sun.COM switch (hdr->code & 0xff) {
128010128SFei.Feng@Sun.COM /* reply to a read command */
128110128SFei.Feng@Sun.COM default:
128210128SFei.Feng@Sun.COM dlen = hdr->len - sizeof (*hdr);
128310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX_CMD, "uath: uath_cmdeof(): "
128410128SFei.Feng@Sun.COM "code %x data len %u\n",
128510128SFei.Feng@Sun.COM hdr->code & 0xff, dlen);
128610128SFei.Feng@Sun.COM
128710128SFei.Feng@Sun.COM /*
128810128SFei.Feng@Sun.COM * The first response from the target after the
128910128SFei.Feng@Sun.COM * HOST_AVAILABLE has an invalid msgid so we must
129010128SFei.Feng@Sun.COM * treat it specially.
129110128SFei.Feng@Sun.COM */
129210128SFei.Feng@Sun.COM if ((hdr->msgid < UATH_CMD_LIST_COUNT) && (hdr->code != 0x13)) {
129310128SFei.Feng@Sun.COM uint32_t *rp = (uint32_t *)(hdr + 1);
129410128SFei.Feng@Sun.COM uint_t olen;
129510128SFei.Feng@Sun.COM
129610128SFei.Feng@Sun.COM if (!(sizeof (*hdr) <= hdr->len &&
129710128SFei.Feng@Sun.COM hdr->len < UATH_MAX_CMDSZ)) {
129810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX_CMD,
129910128SFei.Feng@Sun.COM "uath: uath_cmdeof(): "
130010128SFei.Feng@Sun.COM "invalid WDC msg length %u; "
130110128SFei.Feng@Sun.COM "msg ignored\n",
130210128SFei.Feng@Sun.COM hdr->len);
130310128SFei.Feng@Sun.COM return;
130410128SFei.Feng@Sun.COM }
130510128SFei.Feng@Sun.COM
130610128SFei.Feng@Sun.COM /*
130710128SFei.Feng@Sun.COM * Calculate return/receive payload size; the
130810128SFei.Feng@Sun.COM * first word, if present, always gives the
130910128SFei.Feng@Sun.COM * number of bytes--unless it's 0 in which
131010128SFei.Feng@Sun.COM * case a single 32-bit word should be present.
131110128SFei.Feng@Sun.COM */
131210128SFei.Feng@Sun.COM if (dlen >= sizeof (uint32_t)) {
131310128SFei.Feng@Sun.COM olen = BE_32(rp[0]);
131410128SFei.Feng@Sun.COM dlen -= sizeof (uint32_t);
131510128SFei.Feng@Sun.COM if (olen == 0) {
131610128SFei.Feng@Sun.COM /* convention is 0 =>'s one word */
131710128SFei.Feng@Sun.COM olen = sizeof (uint32_t);
131810128SFei.Feng@Sun.COM /* XXX KASSERT(olen == dlen ) */
131910128SFei.Feng@Sun.COM }
132010128SFei.Feng@Sun.COM } else
132110128SFei.Feng@Sun.COM olen = 0;
132210128SFei.Feng@Sun.COM
132310128SFei.Feng@Sun.COM if (cmd->odata != NULL) {
132410128SFei.Feng@Sun.COM /* NB: cmd->olen validated in uath_cmd */
132510128SFei.Feng@Sun.COM if (olen > cmd->olen) {
132610128SFei.Feng@Sun.COM /* XXX complain? */
132710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX_CMD,
132810128SFei.Feng@Sun.COM "uath: uath_cmdeof(): "
132910128SFei.Feng@Sun.COM "cmd 0x%x olen %u cmd olen %u\n",
133010128SFei.Feng@Sun.COM hdr->code, olen, cmd->olen);
133110128SFei.Feng@Sun.COM olen = cmd->olen;
133210128SFei.Feng@Sun.COM }
133310128SFei.Feng@Sun.COM if (olen > dlen) {
133410128SFei.Feng@Sun.COM /* XXX complain, shouldn't happen */
133510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX_CMD,
133610128SFei.Feng@Sun.COM "uath: uath_cmdeof(): "
133710128SFei.Feng@Sun.COM "cmd 0x%x olen %u dlen %u\n",
133810128SFei.Feng@Sun.COM hdr->code, olen, dlen);
133910128SFei.Feng@Sun.COM olen = dlen;
134010128SFei.Feng@Sun.COM }
134110128SFei.Feng@Sun.COM /* XXX have submitter do this */
134210128SFei.Feng@Sun.COM /* copy answer into caller's supplied buffer */
134310128SFei.Feng@Sun.COM bcopy(&rp[1], cmd->odata, olen);
134410128SFei.Feng@Sun.COM cmd->olen = olen;
134510128SFei.Feng@Sun.COM }
134610128SFei.Feng@Sun.COM }
134710128SFei.Feng@Sun.COM
134810128SFei.Feng@Sun.COM /* Just signal that something happened */
134910128SFei.Feng@Sun.COM uath_cmd_lock_signal(&sc->rlock);
135010128SFei.Feng@Sun.COM break;
135110128SFei.Feng@Sun.COM
135210128SFei.Feng@Sun.COM case WDCMSG_TARGET_START:
135310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX_CMD, "uath: uath_cmdeof(): "
135410128SFei.Feng@Sun.COM "receive TARGET STAERT\n");
135510128SFei.Feng@Sun.COM
135610128SFei.Feng@Sun.COM if (hdr->msgid >= UATH_CMD_LIST_COUNT) {
135710128SFei.Feng@Sun.COM /* XXX */
135810128SFei.Feng@Sun.COM return;
135910128SFei.Feng@Sun.COM }
136010128SFei.Feng@Sun.COM dlen = hdr->len - sizeof (*hdr);
136110128SFei.Feng@Sun.COM if (dlen != sizeof (uint32_t)) {
136210128SFei.Feng@Sun.COM /* XXX something wrong */
136310128SFei.Feng@Sun.COM return;
136410128SFei.Feng@Sun.COM }
136510128SFei.Feng@Sun.COM /* XXX have submitter do this */
136610128SFei.Feng@Sun.COM /* copy answer into caller's supplied buffer */
136710128SFei.Feng@Sun.COM bcopy(hdr + 1, cmd->odata, sizeof (uint32_t));
136810128SFei.Feng@Sun.COM cmd->olen = sizeof (uint32_t);
136910128SFei.Feng@Sun.COM
137010128SFei.Feng@Sun.COM /* wake up caller */
137110128SFei.Feng@Sun.COM uath_cmd_lock_signal(&sc->rlock);
137210128SFei.Feng@Sun.COM break;
137310128SFei.Feng@Sun.COM
137410128SFei.Feng@Sun.COM case WDCMSG_SEND_COMPLETE:
137510128SFei.Feng@Sun.COM /* this notification is sent when UATH_TX_NOTIFY is set */
137610128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX_CMD, "uath: uath_cmdeof(): "
137710128SFei.Feng@Sun.COM "receive Tx notification\n");
137810128SFei.Feng@Sun.COM break;
137910128SFei.Feng@Sun.COM
138010128SFei.Feng@Sun.COM case WDCMSG_TARGET_GET_STATS:
138110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX_CMD, "uath: uath_cmdeof(): "
138210128SFei.Feng@Sun.COM "received device statistics\n");
138310128SFei.Feng@Sun.COM break;
138410128SFei.Feng@Sun.COM }
138510128SFei.Feng@Sun.COM }
138610128SFei.Feng@Sun.COM
138710128SFei.Feng@Sun.COM /* ARGSUSED */
138810128SFei.Feng@Sun.COM static void
uath_cmd_rxeof(usb_pipe_handle_t pipe,usb_bulk_req_t * req)138910128SFei.Feng@Sun.COM uath_cmd_rxeof(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
139010128SFei.Feng@Sun.COM {
139110128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)req->bulk_client_private;
139210128SFei.Feng@Sun.COM struct uath_cmd_hdr *hdr;
139310128SFei.Feng@Sun.COM struct uath_cmd *cmd;
139410128SFei.Feng@Sun.COM mblk_t *m, *mp;
139510128SFei.Feng@Sun.COM int len;
139610128SFei.Feng@Sun.COM
139710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX_CMD, "uath: uath_cmd_rxeof(): "
139810128SFei.Feng@Sun.COM "cr:%s(%d), flags:0x%x, rx queued %d\n",
139910128SFei.Feng@Sun.COM usb_str_cr(req->bulk_completion_reason),
140010128SFei.Feng@Sun.COM req->bulk_completion_reason,
140110128SFei.Feng@Sun.COM req->bulk_cb_flags,
140210128SFei.Feng@Sun.COM sc->rx_cmd_queued);
140310128SFei.Feng@Sun.COM
140410128SFei.Feng@Sun.COM m = req->bulk_data;
140510128SFei.Feng@Sun.COM req->bulk_data = NULL;
140610128SFei.Feng@Sun.COM
140710128SFei.Feng@Sun.COM if (req->bulk_completion_reason != USB_CR_OK) {
140810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX_CMD, "uath: uath_cmd_rxeof(): "
140910128SFei.Feng@Sun.COM "USB CR is not OK\n");
141010128SFei.Feng@Sun.COM goto fail;
141110128SFei.Feng@Sun.COM }
141210128SFei.Feng@Sun.COM
141310128SFei.Feng@Sun.COM if (m->b_cont != NULL) {
141410128SFei.Feng@Sun.COM /* Fragmented message, concatenate */
141510128SFei.Feng@Sun.COM mp = msgpullup(m, -1);
141610128SFei.Feng@Sun.COM freemsg(m);
141710128SFei.Feng@Sun.COM m = mp;
141810128SFei.Feng@Sun.COM mp = NULL;
141910128SFei.Feng@Sun.COM }
142010128SFei.Feng@Sun.COM
142110128SFei.Feng@Sun.COM len = msgdsize(m);
142210128SFei.Feng@Sun.COM if (len < sizeof (struct uath_cmd_hdr)) {
142310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX_CMD, "uath: uath_rx_cmdeof(): "
142410128SFei.Feng@Sun.COM "short xfer error\n");
142510128SFei.Feng@Sun.COM goto fail;
142610128SFei.Feng@Sun.COM }
142710128SFei.Feng@Sun.COM
142810128SFei.Feng@Sun.COM hdr = (struct uath_cmd_hdr *)m->b_rptr;
142910128SFei.Feng@Sun.COM if (BE_32(hdr->code) == 0x13)
143010128SFei.Feng@Sun.COM cmd = &sc->sc_cmd[0];
143110128SFei.Feng@Sun.COM else
143210128SFei.Feng@Sun.COM cmd = &sc->sc_cmd[hdr->msgid];
143310128SFei.Feng@Sun.COM
143410128SFei.Feng@Sun.COM bcopy(m->b_rptr, cmd->buf, len);
143510128SFei.Feng@Sun.COM uath_cmdeof(sc, cmd);
143610128SFei.Feng@Sun.COM (void) uath_rx_cmd_xfer(sc);
143710128SFei.Feng@Sun.COM fail:
143810128SFei.Feng@Sun.COM mutex_enter(&sc->sc_rxlock_cmd);
143910128SFei.Feng@Sun.COM sc->rx_cmd_queued--;
144010128SFei.Feng@Sun.COM mutex_exit(&sc->sc_rxlock_cmd);
144110128SFei.Feng@Sun.COM if (m) freemsg(m);
144210128SFei.Feng@Sun.COM usb_free_bulk_req(req);
144310128SFei.Feng@Sun.COM }
144410128SFei.Feng@Sun.COM
144510128SFei.Feng@Sun.COM static int
uath_rx_cmd_xfer(struct uath_softc * sc)144610128SFei.Feng@Sun.COM uath_rx_cmd_xfer(struct uath_softc *sc)
144710128SFei.Feng@Sun.COM {
144810128SFei.Feng@Sun.COM usb_bulk_req_t *req;
144910128SFei.Feng@Sun.COM int err;
145010128SFei.Feng@Sun.COM
145110128SFei.Feng@Sun.COM req = usb_alloc_bulk_req(sc->sc_dev, UATH_MAX_CMDSZ, USB_FLAGS_SLEEP);
145210128SFei.Feng@Sun.COM if (req == NULL) {
145310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_rx_cmd_xfer(): "
145410128SFei.Feng@Sun.COM "failed to allocate req");
145510128SFei.Feng@Sun.COM return (UATH_FAILURE);
145610128SFei.Feng@Sun.COM }
145710128SFei.Feng@Sun.COM
145810128SFei.Feng@Sun.COM req->bulk_len = UATH_MAX_CMDSZ;
145910128SFei.Feng@Sun.COM req->bulk_client_private = (usb_opaque_t)sc;
146010128SFei.Feng@Sun.COM req->bulk_cb = uath_cmd_rxeof;
146110128SFei.Feng@Sun.COM req->bulk_exc_cb = uath_cmd_rxeof;
146210128SFei.Feng@Sun.COM req->bulk_timeout = 0;
146310128SFei.Feng@Sun.COM req->bulk_completion_reason = 0;
146410128SFei.Feng@Sun.COM req->bulk_cb_flags = 0;
146510128SFei.Feng@Sun.COM req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK
146610128SFei.Feng@Sun.COM | USB_ATTRS_AUTOCLEARING;
146710128SFei.Feng@Sun.COM
146810128SFei.Feng@Sun.COM err = usb_pipe_bulk_xfer(sc->rx_cmd_pipe, req, USB_FLAGS_NOSLEEP);
146910128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
147010128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_rx_cmd_xfer(): "
147110128SFei.Feng@Sun.COM "failed to do rx xfer, %d", err);
147210128SFei.Feng@Sun.COM usb_free_bulk_req(req);
147310128SFei.Feng@Sun.COM return (UATH_FAILURE);
147410128SFei.Feng@Sun.COM }
147510128SFei.Feng@Sun.COM
147610128SFei.Feng@Sun.COM mutex_enter(&sc->sc_rxlock_cmd);
147710128SFei.Feng@Sun.COM sc->rx_cmd_queued++;
147810128SFei.Feng@Sun.COM mutex_exit(&sc->sc_rxlock_cmd);
147910128SFei.Feng@Sun.COM return (UATH_SUCCESS);
148010128SFei.Feng@Sun.COM }
148110128SFei.Feng@Sun.COM
148210128SFei.Feng@Sun.COM static void
uath_init_data_queue(struct uath_softc * sc)148310128SFei.Feng@Sun.COM uath_init_data_queue(struct uath_softc *sc)
148410128SFei.Feng@Sun.COM {
148510128SFei.Feng@Sun.COM sc->tx_data_queued = sc->rx_data_queued = 0;
148610128SFei.Feng@Sun.COM }
148710128SFei.Feng@Sun.COM
148810128SFei.Feng@Sun.COM /* ARGSUSED */
148910128SFei.Feng@Sun.COM static void
uath_data_txeof(usb_pipe_handle_t pipe,usb_bulk_req_t * req)149010128SFei.Feng@Sun.COM uath_data_txeof(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
149110128SFei.Feng@Sun.COM {
149210128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)req->bulk_client_private;
149310128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
149410128SFei.Feng@Sun.COM
149510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_TX, "uath: uath_data_txeof(): "
149610128SFei.Feng@Sun.COM "uath_txeof(): cr:%s(%d), flags:0x%x, tx_data_queued %d\n",
149710128SFei.Feng@Sun.COM usb_str_cr(req->bulk_completion_reason),
149810128SFei.Feng@Sun.COM req->bulk_completion_reason,
149910128SFei.Feng@Sun.COM req->bulk_cb_flags,
150010128SFei.Feng@Sun.COM sc->tx_data_queued);
150110128SFei.Feng@Sun.COM
150210128SFei.Feng@Sun.COM if (req->bulk_completion_reason != USB_CR_OK)
150310128SFei.Feng@Sun.COM sc->sc_tx_err++;
150410128SFei.Feng@Sun.COM
150510128SFei.Feng@Sun.COM mutex_enter(&sc->sc_txlock_data);
150610128SFei.Feng@Sun.COM sc->tx_data_queued--;
150710128SFei.Feng@Sun.COM
150810128SFei.Feng@Sun.COM if (sc->sc_need_sched) {
150910128SFei.Feng@Sun.COM sc->sc_need_sched = 0;
151010128SFei.Feng@Sun.COM mac_tx_update(ic->ic_mach);
151110128SFei.Feng@Sun.COM }
151210128SFei.Feng@Sun.COM
151310128SFei.Feng@Sun.COM mutex_exit(&sc->sc_txlock_data);
151410128SFei.Feng@Sun.COM usb_free_bulk_req(req);
151510128SFei.Feng@Sun.COM }
151610128SFei.Feng@Sun.COM
151710128SFei.Feng@Sun.COM static int
uath_tx_data_xfer(struct uath_softc * sc,mblk_t * mp)151810128SFei.Feng@Sun.COM uath_tx_data_xfer(struct uath_softc *sc, mblk_t *mp)
151910128SFei.Feng@Sun.COM {
152010128SFei.Feng@Sun.COM usb_bulk_req_t *req;
152110128SFei.Feng@Sun.COM int err;
152210128SFei.Feng@Sun.COM
152310128SFei.Feng@Sun.COM req = usb_alloc_bulk_req(sc->sc_dev, 0, USB_FLAGS_SLEEP);
152410128SFei.Feng@Sun.COM if (req == NULL) {
152510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_TX, "uath: uath_tx_data_xfer(): "
152610128SFei.Feng@Sun.COM "uath_tx_data_xfer(): failed to allocate req");
152710128SFei.Feng@Sun.COM freemsg(mp);
152810128SFei.Feng@Sun.COM return (UATH_FAILURE);
152910128SFei.Feng@Sun.COM }
153010128SFei.Feng@Sun.COM
153110128SFei.Feng@Sun.COM req->bulk_len = msgdsize(mp);
153210128SFei.Feng@Sun.COM req->bulk_data = mp;
153310128SFei.Feng@Sun.COM req->bulk_client_private = (usb_opaque_t)sc;
153410128SFei.Feng@Sun.COM req->bulk_timeout = UATH_DATA_TIMEOUT;
153510128SFei.Feng@Sun.COM req->bulk_attributes = USB_ATTRS_AUTOCLEARING;
153610128SFei.Feng@Sun.COM req->bulk_cb = uath_data_txeof;
153710128SFei.Feng@Sun.COM req->bulk_exc_cb = uath_data_txeof;
153810128SFei.Feng@Sun.COM req->bulk_completion_reason = 0;
153910128SFei.Feng@Sun.COM req->bulk_cb_flags = 0;
154010128SFei.Feng@Sun.COM
154110128SFei.Feng@Sun.COM if ((err = usb_pipe_bulk_xfer(sc->tx_data_pipe, req, 0)) !=
154210128SFei.Feng@Sun.COM USB_SUCCESS) {
154310128SFei.Feng@Sun.COM
154410128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_TX, "uath: uath_tx_data_xfer(): "
154510128SFei.Feng@Sun.COM "failed to do tx xfer, %d", err);
154610128SFei.Feng@Sun.COM usb_free_bulk_req(req);
154710128SFei.Feng@Sun.COM return (UATH_FAILURE);
154810128SFei.Feng@Sun.COM }
154910128SFei.Feng@Sun.COM
155010128SFei.Feng@Sun.COM sc->tx_data_queued++;
155110128SFei.Feng@Sun.COM return (UATH_SUCCESS);
155210128SFei.Feng@Sun.COM }
155310128SFei.Feng@Sun.COM
155410128SFei.Feng@Sun.COM /* ARGSUSED */
155510128SFei.Feng@Sun.COM static void
uath_data_rxeof(usb_pipe_handle_t pipe,usb_bulk_req_t * req)155610128SFei.Feng@Sun.COM uath_data_rxeof(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
155710128SFei.Feng@Sun.COM {
155810128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)req->bulk_client_private;
155910128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
156010128SFei.Feng@Sun.COM struct uath_chunk *chunk;
156110128SFei.Feng@Sun.COM struct uath_rx_desc *desc;
156210128SFei.Feng@Sun.COM struct ieee80211_frame *wh;
156310128SFei.Feng@Sun.COM struct ieee80211_node *ni;
156410128SFei.Feng@Sun.COM
156510128SFei.Feng@Sun.COM mblk_t *m, *mp;
156610128SFei.Feng@Sun.COM uint8_t *rxbuf;
156710128SFei.Feng@Sun.COM int actlen, pktlen;
156810128SFei.Feng@Sun.COM
156910128SFei.Feng@Sun.COM mutex_enter(&sc->sc_rxlock_data);
157010128SFei.Feng@Sun.COM
157110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX, "uath: uath_data_rxeof(): "
157210128SFei.Feng@Sun.COM "cr:%s(%d), flags:0x%x, rx_data_queued %d\n",
157310128SFei.Feng@Sun.COM usb_str_cr(req->bulk_completion_reason),
157410128SFei.Feng@Sun.COM req->bulk_completion_reason,
157510128SFei.Feng@Sun.COM req->bulk_cb_flags,
157610128SFei.Feng@Sun.COM sc->rx_data_queued);
157710128SFei.Feng@Sun.COM
157810128SFei.Feng@Sun.COM mp = req->bulk_data;
157910128SFei.Feng@Sun.COM req->bulk_data = NULL;
158010128SFei.Feng@Sun.COM
158110128SFei.Feng@Sun.COM if (req->bulk_completion_reason != USB_CR_OK) {
158210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX, "uath: uath_data_rxeof(): "
158310128SFei.Feng@Sun.COM "USB CR is not OK\n");
158410128SFei.Feng@Sun.COM sc->sc_rx_err++;
158510128SFei.Feng@Sun.COM goto fail;
158610128SFei.Feng@Sun.COM }
158710128SFei.Feng@Sun.COM
158810128SFei.Feng@Sun.COM rxbuf = (uint8_t *)mp->b_rptr;
158910128SFei.Feng@Sun.COM actlen = (uintptr_t)mp->b_wptr - (uintptr_t)mp->b_rptr;
159010128SFei.Feng@Sun.COM if (actlen < UATH_MIN_RXBUFSZ) {
159110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX, "uath_data_rxeof(): "
159210128SFei.Feng@Sun.COM "wrong recv size %d\n", actlen);
159310128SFei.Feng@Sun.COM sc->sc_rx_err++;
159410128SFei.Feng@Sun.COM goto fail;
159510128SFei.Feng@Sun.COM }
159610128SFei.Feng@Sun.COM
159710128SFei.Feng@Sun.COM chunk = (struct uath_chunk *)rxbuf;
159810128SFei.Feng@Sun.COM if (chunk->seqnum == 0 && chunk->flags == 0 && chunk->length == 0) {
159910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX, "uath: uath_data_rxeof(): "
160010128SFei.Feng@Sun.COM "strange response\n");
160110128SFei.Feng@Sun.COM UATH_RESET_INTRX(sc);
160210128SFei.Feng@Sun.COM sc->sc_rx_err++;
160310128SFei.Feng@Sun.COM goto fail;
160410128SFei.Feng@Sun.COM }
160510128SFei.Feng@Sun.COM
160610128SFei.Feng@Sun.COM if (chunk->seqnum != sc->sc_intrx_nextnum) {
160710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX, "uath: uath_data_rxeof(): "
160810128SFei.Feng@Sun.COM "invalid seqnum %d, expected %d\n",
160910128SFei.Feng@Sun.COM chunk->seqnum, sc->sc_intrx_nextnum);
161010128SFei.Feng@Sun.COM UATH_STAT_INC(sc, st_badchunkseqnum);
161110128SFei.Feng@Sun.COM UATH_RESET_INTRX(sc);
161210128SFei.Feng@Sun.COM sc->sc_rx_err++;
161310128SFei.Feng@Sun.COM goto fail;
161410128SFei.Feng@Sun.COM }
161510128SFei.Feng@Sun.COM
161610128SFei.Feng@Sun.COM /* check multi-chunk frames */
161710128SFei.Feng@Sun.COM if ((chunk->seqnum == 0 && !(chunk->flags & UATH_CFLAGS_FINAL)) ||
161810128SFei.Feng@Sun.COM (chunk->seqnum != 0 && (chunk->flags & UATH_CFLAGS_FINAL)) ||
161910128SFei.Feng@Sun.COM chunk->flags & UATH_CFLAGS_RXMSG) {
162010128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX, "uath: uath_data_rxeof(): "
162110128SFei.Feng@Sun.COM "receive multi-chunk frames "
162210128SFei.Feng@Sun.COM "chunk seqnum %x, flags %x, length %u\n",
162310128SFei.Feng@Sun.COM chunk->seqnum, chunk->flags, BE_16(chunk->length));
162410128SFei.Feng@Sun.COM UATH_STAT_INC(sc, st_multichunk);
162510128SFei.Feng@Sun.COM }
162610128SFei.Feng@Sun.COM
162710128SFei.Feng@Sun.COM
162810128SFei.Feng@Sun.COM /* if the frame is not final continue the transfer */
162910128SFei.Feng@Sun.COM if (!(chunk->flags & UATH_CFLAGS_FINAL))
163010128SFei.Feng@Sun.COM sc->sc_intrx_nextnum++;
163110128SFei.Feng@Sun.COM
163210128SFei.Feng@Sun.COM /*
163310128SFei.Feng@Sun.COM * if the frame is not set UATH_CFLAGS_RXMSG, then rx descriptor is
163410128SFei.Feng@Sun.COM * located at the end, 32-bit aligned
163510128SFei.Feng@Sun.COM */
163610128SFei.Feng@Sun.COM desc = (chunk->flags & UATH_CFLAGS_RXMSG) ?
163710128SFei.Feng@Sun.COM (struct uath_rx_desc *)(chunk + 1) :
163810128SFei.Feng@Sun.COM (struct uath_rx_desc *)(((uint8_t *)chunk) +
163910128SFei.Feng@Sun.COM sizeof (struct uath_chunk) + BE_16(chunk->length) -
164010128SFei.Feng@Sun.COM sizeof (struct uath_rx_desc));
164110128SFei.Feng@Sun.COM
164210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX, "uath: uath_data_rxeof(): "
164310128SFei.Feng@Sun.COM "frame len %u code %u status %u rate %u antenna %u "
164410128SFei.Feng@Sun.COM "rssi %d channel %u phyerror %u connix %u "
164510128SFei.Feng@Sun.COM "decrypterror %u keycachemiss %u\n",
164610128SFei.Feng@Sun.COM BE_32(desc->framelen), BE_32(desc->code), BE_32(desc->status),
164710128SFei.Feng@Sun.COM BE_32(desc->rate), BE_32(desc->antenna), BE_32(desc->rssi),
164810128SFei.Feng@Sun.COM BE_32(desc->channel), BE_32(desc->phyerror), BE_32(desc->connix),
164910128SFei.Feng@Sun.COM BE_32(desc->decrypterror), BE_32(desc->keycachemiss));
165010128SFei.Feng@Sun.COM
165110128SFei.Feng@Sun.COM if (BE_32(desc->len) > IEEE80211_MAX_LEN) {
165210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX, "uath: uath_data_rxeof(): "
165310128SFei.Feng@Sun.COM "bad descriptor (len=%d)\n", BE_32(desc->len));
165410128SFei.Feng@Sun.COM UATH_STAT_INC(sc, st_toobigrxpkt);
165510128SFei.Feng@Sun.COM goto fail;
165610128SFei.Feng@Sun.COM }
165710128SFei.Feng@Sun.COM
165810128SFei.Feng@Sun.COM uath_update_rxstat(sc, BE_32(desc->status));
165910128SFei.Feng@Sun.COM
166010128SFei.Feng@Sun.COM pktlen = BE_32(desc->framelen) - UATH_RX_DUMMYSIZE;
166110128SFei.Feng@Sun.COM
166210128SFei.Feng@Sun.COM if ((m = allocb(pktlen, BPRI_MED)) == NULL) {
166310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_RX, "uath: uath_data_rxeof(): "
166410128SFei.Feng@Sun.COM "allocate mblk failed.\n");
166510128SFei.Feng@Sun.COM sc->sc_rx_nobuf++;
166610128SFei.Feng@Sun.COM goto fail;
166710128SFei.Feng@Sun.COM }
166810128SFei.Feng@Sun.COM bcopy((rxbuf + sizeof (struct uath_chunk)), m->b_rptr, pktlen);
166910128SFei.Feng@Sun.COM
167010128SFei.Feng@Sun.COM m->b_wptr = m->b_rptr + pktlen;
167110128SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr;
167210128SFei.Feng@Sun.COM ni = ieee80211_find_rxnode(ic, wh);
167310128SFei.Feng@Sun.COM
167410128SFei.Feng@Sun.COM /* send the frame to the 802.11 layer */
167510128SFei.Feng@Sun.COM (void) ieee80211_input(ic, m, ni, (int)BE_32(desc->rssi), 0);
167610128SFei.Feng@Sun.COM
167710128SFei.Feng@Sun.COM /* node is no longer needed */
167810128SFei.Feng@Sun.COM ieee80211_free_node(ni);
167910128SFei.Feng@Sun.COM fail:
168010128SFei.Feng@Sun.COM sc->rx_data_queued--;
168110128SFei.Feng@Sun.COM if (mp) freemsg(mp);
168210128SFei.Feng@Sun.COM usb_free_bulk_req(req);
168310128SFei.Feng@Sun.COM mutex_exit(&sc->sc_rxlock_data);
168410128SFei.Feng@Sun.COM if (UATH_IS_RUNNING(sc) && !UATH_IS_SUSPEND(sc)) {
168510128SFei.Feng@Sun.COM (void) uath_rx_data_xfer(sc);
168610128SFei.Feng@Sun.COM }
168710128SFei.Feng@Sun.COM }
168810128SFei.Feng@Sun.COM
168910128SFei.Feng@Sun.COM static int
uath_rx_data_xfer(struct uath_softc * sc)169010128SFei.Feng@Sun.COM uath_rx_data_xfer(struct uath_softc *sc)
169110128SFei.Feng@Sun.COM {
169210128SFei.Feng@Sun.COM usb_bulk_req_t *req;
169310128SFei.Feng@Sun.COM int err;
169410128SFei.Feng@Sun.COM
169510128SFei.Feng@Sun.COM req = usb_alloc_bulk_req(sc->sc_dev,
169610128SFei.Feng@Sun.COM IEEE80211_MAX_LEN, USB_FLAGS_SLEEP);
169710128SFei.Feng@Sun.COM if (req == NULL) {
169810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_rx_data_xfer(): "
169910128SFei.Feng@Sun.COM "failed to allocate req");
170010128SFei.Feng@Sun.COM return (UATH_SUCCESS);
170110128SFei.Feng@Sun.COM }
170210128SFei.Feng@Sun.COM
170310128SFei.Feng@Sun.COM req->bulk_len = IEEE80211_MAX_LEN;
170410128SFei.Feng@Sun.COM req->bulk_cb = uath_data_rxeof;
170510128SFei.Feng@Sun.COM req->bulk_exc_cb = uath_data_rxeof;
170610128SFei.Feng@Sun.COM req->bulk_client_private = (usb_opaque_t)sc;
170710128SFei.Feng@Sun.COM req->bulk_timeout = 0;
170810128SFei.Feng@Sun.COM req->bulk_completion_reason = 0;
170910128SFei.Feng@Sun.COM req->bulk_cb_flags = 0;
171010128SFei.Feng@Sun.COM req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK
171110128SFei.Feng@Sun.COM | USB_ATTRS_AUTOCLEARING;
171210128SFei.Feng@Sun.COM
171310128SFei.Feng@Sun.COM err = usb_pipe_bulk_xfer(sc->rx_data_pipe, req, 0);
171410128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
171510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_rx_data_xfer(): "
171610128SFei.Feng@Sun.COM "failed to do rx xfer, %d", err);
171710128SFei.Feng@Sun.COM usb_free_bulk_req(req);
171810128SFei.Feng@Sun.COM return (UATH_FAILURE);
171910128SFei.Feng@Sun.COM }
172010128SFei.Feng@Sun.COM
172110128SFei.Feng@Sun.COM mutex_enter(&sc->sc_rxlock_data);
172210128SFei.Feng@Sun.COM sc->rx_data_queued++;
172310128SFei.Feng@Sun.COM mutex_exit(&sc->sc_rxlock_data);
172410128SFei.Feng@Sun.COM return (UATH_SUCCESS);
172510128SFei.Feng@Sun.COM }
172610128SFei.Feng@Sun.COM
172710128SFei.Feng@Sun.COM static void
uath_update_rxstat(struct uath_softc * sc,uint32_t status)172810128SFei.Feng@Sun.COM uath_update_rxstat(struct uath_softc *sc, uint32_t status)
172910128SFei.Feng@Sun.COM {
173010128SFei.Feng@Sun.COM
173110128SFei.Feng@Sun.COM switch (status) {
173210128SFei.Feng@Sun.COM case UATH_STATUS_STOP_IN_PROGRESS:
173310128SFei.Feng@Sun.COM UATH_STAT_INC(sc, st_stopinprogress);
173410128SFei.Feng@Sun.COM break;
173510128SFei.Feng@Sun.COM case UATH_STATUS_CRC_ERR:
173610128SFei.Feng@Sun.COM UATH_STAT_INC(sc, st_crcerr);
173710128SFei.Feng@Sun.COM break;
173810128SFei.Feng@Sun.COM case UATH_STATUS_PHY_ERR:
173910128SFei.Feng@Sun.COM UATH_STAT_INC(sc, st_phyerr);
174010128SFei.Feng@Sun.COM break;
174110128SFei.Feng@Sun.COM case UATH_STATUS_DECRYPT_CRC_ERR:
174210128SFei.Feng@Sun.COM UATH_STAT_INC(sc, st_decrypt_crcerr);
174310128SFei.Feng@Sun.COM break;
174410128SFei.Feng@Sun.COM case UATH_STATUS_DECRYPT_MIC_ERR:
174510128SFei.Feng@Sun.COM UATH_STAT_INC(sc, st_decrypt_micerr);
174610128SFei.Feng@Sun.COM break;
174710128SFei.Feng@Sun.COM case UATH_STATUS_DECOMP_ERR:
174810128SFei.Feng@Sun.COM UATH_STAT_INC(sc, st_decomperr);
174910128SFei.Feng@Sun.COM break;
175010128SFei.Feng@Sun.COM case UATH_STATUS_KEY_ERR:
175110128SFei.Feng@Sun.COM UATH_STAT_INC(sc, st_keyerr);
175210128SFei.Feng@Sun.COM break;
175310128SFei.Feng@Sun.COM case UATH_STATUS_ERR:
175410128SFei.Feng@Sun.COM UATH_STAT_INC(sc, st_err);
175510128SFei.Feng@Sun.COM break;
175610128SFei.Feng@Sun.COM default:
175710128SFei.Feng@Sun.COM break;
175810128SFei.Feng@Sun.COM }
175910128SFei.Feng@Sun.COM }
176010128SFei.Feng@Sun.COM
176110128SFei.Feng@Sun.COM static void
uath_next_scan(void * arg)176210128SFei.Feng@Sun.COM uath_next_scan(void *arg)
176310128SFei.Feng@Sun.COM {
176410128SFei.Feng@Sun.COM struct uath_softc *sc = arg;
176510128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
176610128SFei.Feng@Sun.COM
176710128SFei.Feng@Sun.COM if (ic->ic_state == IEEE80211_S_SCAN)
176810128SFei.Feng@Sun.COM ieee80211_next_scan(ic);
176910128SFei.Feng@Sun.COM
177010128SFei.Feng@Sun.COM sc->sc_scan_id = 0;
177110128SFei.Feng@Sun.COM }
177210128SFei.Feng@Sun.COM
177310128SFei.Feng@Sun.COM static int
uath_create_connection(struct uath_softc * sc,uint32_t connid)177410128SFei.Feng@Sun.COM uath_create_connection(struct uath_softc *sc, uint32_t connid)
177510128SFei.Feng@Sun.COM {
177610128SFei.Feng@Sun.COM const struct ieee80211_rateset *rs;
177710128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
177810128SFei.Feng@Sun.COM struct ieee80211_node *ni = ic->ic_bss;
177910128SFei.Feng@Sun.COM struct uath_cmd_create_connection create;
178010128SFei.Feng@Sun.COM int err;
178110128SFei.Feng@Sun.COM
178210128SFei.Feng@Sun.COM bzero(&create, sizeof (create));
178310128SFei.Feng@Sun.COM create.connid = BE_32(connid);
178410128SFei.Feng@Sun.COM create.bssid = BE_32(0);
178510128SFei.Feng@Sun.COM /* XXX packed or not? */
178610128SFei.Feng@Sun.COM create.size = BE_32(sizeof (struct uath_cmd_rateset));
178710128SFei.Feng@Sun.COM
178810128SFei.Feng@Sun.COM rs = &ni->in_rates;
178910128SFei.Feng@Sun.COM create.connattr.rateset.length = rs->ir_nrates;
179010128SFei.Feng@Sun.COM bcopy(rs->ir_rates, &create.connattr.rateset.set[0],
179110128SFei.Feng@Sun.COM rs->ir_nrates);
179210128SFei.Feng@Sun.COM
179310128SFei.Feng@Sun.COM /* XXX turbo */
179410128SFei.Feng@Sun.COM if (UATH_IS_CHAN_A(ni->in_chan))
179510128SFei.Feng@Sun.COM create.connattr.wlanmode = BE_32(WLAN_MODE_11a);
179610128SFei.Feng@Sun.COM else if (UATH_IS_CHAN_ANYG(ni->in_chan))
179710128SFei.Feng@Sun.COM create.connattr.wlanmode = BE_32(WLAN_MODE_11g);
179810128SFei.Feng@Sun.COM else
179910128SFei.Feng@Sun.COM create.connattr.wlanmode = BE_32(WLAN_MODE_11b);
180010128SFei.Feng@Sun.COM
180110128SFei.Feng@Sun.COM err = uath_cmd_write(sc, WDCMSG_CREATE_CONNECTION, &create,
180210128SFei.Feng@Sun.COM sizeof (create), 0);
180310128SFei.Feng@Sun.COM return (err);
180410128SFei.Feng@Sun.COM }
180510128SFei.Feng@Sun.COM
180610128SFei.Feng@Sun.COM static int
uath_set_rates(struct uath_softc * sc,const struct ieee80211_rateset * rs)180710128SFei.Feng@Sun.COM uath_set_rates(struct uath_softc *sc, const struct ieee80211_rateset *rs)
180810128SFei.Feng@Sun.COM {
180910128SFei.Feng@Sun.COM struct uath_cmd_rates rates;
181010128SFei.Feng@Sun.COM int err;
181110128SFei.Feng@Sun.COM
181210128SFei.Feng@Sun.COM bzero(&rates, sizeof (rates));
181310128SFei.Feng@Sun.COM rates.connid = BE_32(UATH_ID_BSS); /* XXX */
181410128SFei.Feng@Sun.COM rates.size = BE_32(sizeof (struct uath_cmd_rateset));
181510128SFei.Feng@Sun.COM /* XXX bounds check rs->rs_nrates */
181610128SFei.Feng@Sun.COM rates.rateset.length = rs->ir_nrates;
181710128SFei.Feng@Sun.COM bcopy(rs->ir_rates, &rates.rateset.set[0], rs->ir_nrates);
181810128SFei.Feng@Sun.COM
181910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_set_rates(): "
182010128SFei.Feng@Sun.COM "setting supported rates nrates=%d\n", rs->ir_nrates);
182110128SFei.Feng@Sun.COM err = uath_cmd_write(sc, WDCMSG_SET_BASIC_RATE,
182210128SFei.Feng@Sun.COM &rates, sizeof (rates), 0);
182310128SFei.Feng@Sun.COM return (err);
182410128SFei.Feng@Sun.COM }
182510128SFei.Feng@Sun.COM
182610128SFei.Feng@Sun.COM static int
uath_write_associd(struct uath_softc * sc)182710128SFei.Feng@Sun.COM uath_write_associd(struct uath_softc *sc)
182810128SFei.Feng@Sun.COM {
182910128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
183010128SFei.Feng@Sun.COM struct ieee80211_node *ni = ic->ic_bss;
183110128SFei.Feng@Sun.COM struct uath_cmd_set_associd associd;
183210128SFei.Feng@Sun.COM int err;
183310128SFei.Feng@Sun.COM
183410128SFei.Feng@Sun.COM bzero(&associd, sizeof (associd));
183510128SFei.Feng@Sun.COM associd.defaultrateix = BE_32(1); /* XXX */
183610128SFei.Feng@Sun.COM associd.associd = BE_32(ni->in_associd);
183710128SFei.Feng@Sun.COM associd.timoffset = BE_32(0x3b); /* XXX */
183810128SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(associd.bssid, ni->in_bssid);
183910128SFei.Feng@Sun.COM err = uath_cmd_write(sc, WDCMSG_WRITE_ASSOCID, &associd,
184010128SFei.Feng@Sun.COM sizeof (associd), 0);
184110128SFei.Feng@Sun.COM return (err);
184210128SFei.Feng@Sun.COM }
184310128SFei.Feng@Sun.COM
184410128SFei.Feng@Sun.COM static int
uath_set_ledsteady(struct uath_softc * sc,int lednum,int ledmode)184510128SFei.Feng@Sun.COM uath_set_ledsteady(struct uath_softc *sc, int lednum, int ledmode)
184610128SFei.Feng@Sun.COM {
184710128SFei.Feng@Sun.COM struct uath_cmd_ledsteady led;
184810128SFei.Feng@Sun.COM int err;
184910128SFei.Feng@Sun.COM
185010128SFei.Feng@Sun.COM led.lednum = BE_32(lednum);
185110128SFei.Feng@Sun.COM led.ledmode = BE_32(ledmode);
185210128SFei.Feng@Sun.COM
185310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_set_ledsteady(): "
185410128SFei.Feng@Sun.COM "set %s led %s (steady)\n",
185510128SFei.Feng@Sun.COM (lednum == UATH_LED_LINK) ? "link" : "activity",
185610128SFei.Feng@Sun.COM ledmode ? "on" : "off");
185710128SFei.Feng@Sun.COM err = uath_cmd_write(sc, WDCMSG_SET_LED_STEADY, &led, sizeof (led), 0);
185810128SFei.Feng@Sun.COM return (err);
185910128SFei.Feng@Sun.COM }
186010128SFei.Feng@Sun.COM
186110128SFei.Feng@Sun.COM static int
uath_set_ledblink(struct uath_softc * sc,int lednum,int ledmode,int blinkrate,int slowmode)186210128SFei.Feng@Sun.COM uath_set_ledblink(struct uath_softc *sc, int lednum, int ledmode,
186310128SFei.Feng@Sun.COM int blinkrate, int slowmode)
186410128SFei.Feng@Sun.COM {
186510128SFei.Feng@Sun.COM struct uath_cmd_ledblink led;
186610128SFei.Feng@Sun.COM int err;
186710128SFei.Feng@Sun.COM
186810128SFei.Feng@Sun.COM led.lednum = BE_32(lednum);
186910128SFei.Feng@Sun.COM led.ledmode = BE_32(ledmode);
187010128SFei.Feng@Sun.COM led.blinkrate = BE_32(blinkrate);
187110128SFei.Feng@Sun.COM led.slowmode = BE_32(slowmode);
187210128SFei.Feng@Sun.COM
187310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_set_ledblink(): "
187410128SFei.Feng@Sun.COM "set %s led %s (blink)\n",
187510128SFei.Feng@Sun.COM (lednum == UATH_LED_LINK) ? "link" : "activity",
187610128SFei.Feng@Sun.COM ledmode ? "on" : "off");
187710128SFei.Feng@Sun.COM
187810128SFei.Feng@Sun.COM err = uath_cmd_write(sc, WDCMSG_SET_LED_BLINK,
187910128SFei.Feng@Sun.COM &led, sizeof (led), 0);
188010128SFei.Feng@Sun.COM return (err);
188110128SFei.Feng@Sun.COM }
188210128SFei.Feng@Sun.COM
188310128SFei.Feng@Sun.COM
188410128SFei.Feng@Sun.COM static int
uath_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)188510128SFei.Feng@Sun.COM uath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
188610128SFei.Feng@Sun.COM {
188710128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)ic;
188810128SFei.Feng@Sun.COM struct ieee80211_node *ni = ic->ic_bss;
188910128SFei.Feng@Sun.COM enum ieee80211_state ostate;
189010128SFei.Feng@Sun.COM int err;
189110128SFei.Feng@Sun.COM
189210128SFei.Feng@Sun.COM ostate = ic->ic_state;
189310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_newstate(): "
189410128SFei.Feng@Sun.COM "%d -> %d\n", ostate, nstate);
189510128SFei.Feng@Sun.COM
189610128SFei.Feng@Sun.COM if (sc->sc_scan_id != 0) {
189710128SFei.Feng@Sun.COM (void) untimeout(sc->sc_scan_id);
189810128SFei.Feng@Sun.COM sc->sc_scan_id = 0;
189910128SFei.Feng@Sun.COM }
190010128SFei.Feng@Sun.COM
190110128SFei.Feng@Sun.COM UATH_LOCK(sc);
190210128SFei.Feng@Sun.COM
190310128SFei.Feng@Sun.COM if (UATH_IS_DISCONNECT(sc) && (nstate != IEEE80211_S_INIT)) {
190410128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
190510128SFei.Feng@Sun.COM return (DDI_SUCCESS);
190610128SFei.Feng@Sun.COM }
190710128SFei.Feng@Sun.COM
190810128SFei.Feng@Sun.COM if (UATH_IS_SUSPEND(sc) && (nstate != IEEE80211_S_INIT)) {
190910128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
191010128SFei.Feng@Sun.COM return (DDI_SUCCESS);
191110128SFei.Feng@Sun.COM }
191210128SFei.Feng@Sun.COM
191310128SFei.Feng@Sun.COM switch (nstate) {
191410128SFei.Feng@Sun.COM case IEEE80211_S_INIT:
191510128SFei.Feng@Sun.COM if (ostate == IEEE80211_S_RUN) {
191610128SFei.Feng@Sun.COM /* turn link and activity LEDs off */
191710128SFei.Feng@Sun.COM (void) uath_set_ledstate(sc, 0);
191810128SFei.Feng@Sun.COM }
191910128SFei.Feng@Sun.COM break;
192010128SFei.Feng@Sun.COM case IEEE80211_S_SCAN:
192110128SFei.Feng@Sun.COM if (uath_switch_channel(sc, ic->ic_curchan) != UATH_SUCCESS) {
192210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_newstate(): "
192310128SFei.Feng@Sun.COM "could not switch channel\n");
192410128SFei.Feng@Sun.COM break;
192510128SFei.Feng@Sun.COM }
192610128SFei.Feng@Sun.COM sc->sc_scan_id = timeout(uath_next_scan, (void *)sc,
192710128SFei.Feng@Sun.COM drv_usectohz(250000));
192810128SFei.Feng@Sun.COM break;
192910128SFei.Feng@Sun.COM case IEEE80211_S_AUTH:
193010128SFei.Feng@Sun.COM /* XXX good place? set RTS threshold */
193110128SFei.Feng@Sun.COM uath_config(sc, CFG_USER_RTS_THRESHOLD, ic->ic_rtsthreshold);
193210128SFei.Feng@Sun.COM
193310128SFei.Feng@Sun.COM if (uath_switch_channel(sc, ni->in_chan) != 0) {
193410128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_newstate(): "
193510128SFei.Feng@Sun.COM "could not switch channel\n");
193610128SFei.Feng@Sun.COM break;
193710128SFei.Feng@Sun.COM }
193810128SFei.Feng@Sun.COM if (uath_create_connection(sc, UATH_ID_BSS) != 0) {
193910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_newstate(): "
194010128SFei.Feng@Sun.COM "could not create connection\n");
194110128SFei.Feng@Sun.COM break;
194210128SFei.Feng@Sun.COM }
194310128SFei.Feng@Sun.COM break;
194410128SFei.Feng@Sun.COM case IEEE80211_S_ASSOC:
194510128SFei.Feng@Sun.COM if (uath_set_rates(sc, &ni->in_rates) != 0) {
194610128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_newstate(): "
194710128SFei.Feng@Sun.COM "could not set negotiated rate set\n");
194810128SFei.Feng@Sun.COM break;
194910128SFei.Feng@Sun.COM }
195010128SFei.Feng@Sun.COM break;
195110128SFei.Feng@Sun.COM case IEEE80211_S_RUN:
195210128SFei.Feng@Sun.COM /* XXX monitor mode doesn't be supported */
195310128SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_MONITOR) {
195410128SFei.Feng@Sun.COM (void) uath_set_ledstate(sc, 1);
195510128SFei.Feng@Sun.COM break;
195610128SFei.Feng@Sun.COM }
195710128SFei.Feng@Sun.COM
195810128SFei.Feng@Sun.COM /*
195910128SFei.Feng@Sun.COM * Tx rate is controlled by firmware, report the maximum
196010128SFei.Feng@Sun.COM * negotiated rate in ifconfig output.
196110128SFei.Feng@Sun.COM */
196210128SFei.Feng@Sun.COM ni->in_txrate = ni->in_rates.ir_nrates - 1;
196310128SFei.Feng@Sun.COM
196410128SFei.Feng@Sun.COM if (uath_write_associd(sc) != 0) {
196510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_newstate(): "
196610128SFei.Feng@Sun.COM "could not write association id\n");
196710128SFei.Feng@Sun.COM break;
196810128SFei.Feng@Sun.COM }
196910128SFei.Feng@Sun.COM /* turn link LED on */
197010128SFei.Feng@Sun.COM (void) uath_set_ledsteady(sc, UATH_LED_LINK, UATH_LED_ON);
197110128SFei.Feng@Sun.COM /* make activity LED blink */
197210128SFei.Feng@Sun.COM (void) uath_set_ledblink(sc, UATH_LED_ACTIVITY,
197310128SFei.Feng@Sun.COM UATH_LED_ON, 1, 2);
197410128SFei.Feng@Sun.COM /* set state to associated */
197510128SFei.Feng@Sun.COM (void) uath_set_ledstate(sc, 1);
197610128SFei.Feng@Sun.COM break;
197710128SFei.Feng@Sun.COM }
197810128SFei.Feng@Sun.COM
197910128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
198010128SFei.Feng@Sun.COM
198110128SFei.Feng@Sun.COM err = sc->sc_newstate(ic, nstate, arg);
198210128SFei.Feng@Sun.COM return (err);
198310128SFei.Feng@Sun.COM }
198410128SFei.Feng@Sun.COM
198510128SFei.Feng@Sun.COM static int
uath_send(ieee80211com_t * ic,mblk_t * mp,uint8_t type)198610128SFei.Feng@Sun.COM uath_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
198710128SFei.Feng@Sun.COM {
198810128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)ic;
198910128SFei.Feng@Sun.COM struct uath_chunk *chunk;
199010128SFei.Feng@Sun.COM struct uath_tx_desc *desc;
199110128SFei.Feng@Sun.COM struct ieee80211_frame *wh;
199210128SFei.Feng@Sun.COM struct ieee80211_node *ni = NULL;
199310128SFei.Feng@Sun.COM struct ieee80211_key *k;
199410128SFei.Feng@Sun.COM
199510128SFei.Feng@Sun.COM mblk_t *m, *m0;
199610128SFei.Feng@Sun.COM int err, off, mblen;
199710128SFei.Feng@Sun.COM int pktlen, framelen, msglen;
199810128SFei.Feng@Sun.COM
199910128SFei.Feng@Sun.COM err = UATH_SUCCESS;
200010128SFei.Feng@Sun.COM
200110128SFei.Feng@Sun.COM mutex_enter(&sc->sc_txlock_data);
200210128SFei.Feng@Sun.COM
200310128SFei.Feng@Sun.COM if (UATH_IS_SUSPEND(sc)) {
200410128SFei.Feng@Sun.COM err = 0;
200510128SFei.Feng@Sun.COM goto fail;
200610128SFei.Feng@Sun.COM }
200710128SFei.Feng@Sun.COM
200810128SFei.Feng@Sun.COM if (sc->tx_data_queued > UATH_TX_DATA_LIST_COUNT) {
200910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_TX, "uath: uath_send(): "
201010128SFei.Feng@Sun.COM "no TX buffer available!\n");
201110128SFei.Feng@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) ==
201210128SFei.Feng@Sun.COM IEEE80211_FC0_TYPE_DATA) {
201310128SFei.Feng@Sun.COM sc->sc_need_sched = 1;
201410128SFei.Feng@Sun.COM }
201510128SFei.Feng@Sun.COM sc->sc_tx_nobuf++;
201610128SFei.Feng@Sun.COM err = ENOMEM;
201710128SFei.Feng@Sun.COM goto fail;
201810128SFei.Feng@Sun.COM }
201910128SFei.Feng@Sun.COM
202010128SFei.Feng@Sun.COM m = allocb(UATH_MAX_TXBUFSZ, BPRI_MED);
202110128SFei.Feng@Sun.COM if (m == NULL) {
202210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_TX, "uath: uath_send(): "
202310128SFei.Feng@Sun.COM "can't alloc mblk.\n");
202410128SFei.Feng@Sun.COM err = DDI_FAILURE;
202510128SFei.Feng@Sun.COM goto fail;
202610128SFei.Feng@Sun.COM }
202710128SFei.Feng@Sun.COM
202810128SFei.Feng@Sun.COM /* skip TX descriptor */
202910128SFei.Feng@Sun.COM m->b_rptr += sizeof (struct uath_chunk) + sizeof (struct uath_tx_desc);
203010128SFei.Feng@Sun.COM m->b_wptr += sizeof (struct uath_chunk) + sizeof (struct uath_tx_desc);
203110128SFei.Feng@Sun.COM
203210128SFei.Feng@Sun.COM for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) {
203310128SFei.Feng@Sun.COM mblen = (uintptr_t)m0->b_wptr - (uintptr_t)m0->b_rptr;
203410128SFei.Feng@Sun.COM (void) memcpy(m->b_rptr + off, m0->b_rptr, mblen);
203510128SFei.Feng@Sun.COM off += mblen;
203610128SFei.Feng@Sun.COM }
203710128SFei.Feng@Sun.COM m->b_wptr += off;
203810128SFei.Feng@Sun.COM
203910128SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr;
204010128SFei.Feng@Sun.COM
204110128SFei.Feng@Sun.COM ni = ieee80211_find_txnode(ic, wh->i_addr1);
204210128SFei.Feng@Sun.COM if (ni == NULL) {
204310128SFei.Feng@Sun.COM err = DDI_FAILURE;
204410128SFei.Feng@Sun.COM freemsg(m);
204510128SFei.Feng@Sun.COM goto fail;
204610128SFei.Feng@Sun.COM }
204710128SFei.Feng@Sun.COM
204810128SFei.Feng@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) ==
204910128SFei.Feng@Sun.COM IEEE80211_FC0_TYPE_DATA) {
205010128SFei.Feng@Sun.COM (void) ieee80211_encap(ic, m, ni);
205110128SFei.Feng@Sun.COM }
205210128SFei.Feng@Sun.COM
205310128SFei.Feng@Sun.COM if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
205410128SFei.Feng@Sun.COM k = ieee80211_crypto_encap(ic, m);
205510128SFei.Feng@Sun.COM if (k == NULL) {
205610128SFei.Feng@Sun.COM freemsg(m);
205710128SFei.Feng@Sun.COM err = DDI_FAILURE;
205810128SFei.Feng@Sun.COM goto fail;
205910128SFei.Feng@Sun.COM }
206010128SFei.Feng@Sun.COM /* packet header may have moved, reset our local pointer */
206110128SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr;
206210128SFei.Feng@Sun.COM }
206310128SFei.Feng@Sun.COM
206410128SFei.Feng@Sun.COM pktlen = (uintptr_t)m->b_wptr - (uintptr_t)m->b_rptr;
206510128SFei.Feng@Sun.COM framelen = pktlen + IEEE80211_CRC_LEN;
206610128SFei.Feng@Sun.COM msglen = framelen + sizeof (struct uath_tx_desc);
206710128SFei.Feng@Sun.COM
206810128SFei.Feng@Sun.COM m->b_rptr -= sizeof (struct uath_chunk) + sizeof (struct uath_tx_desc);
206910128SFei.Feng@Sun.COM
207010128SFei.Feng@Sun.COM chunk = (struct uath_chunk *)m->b_rptr;
207110128SFei.Feng@Sun.COM desc = (struct uath_tx_desc *)(chunk + 1);
207210128SFei.Feng@Sun.COM
207310128SFei.Feng@Sun.COM /* one chunk only for now */
207410128SFei.Feng@Sun.COM chunk->seqnum = 0;
207510128SFei.Feng@Sun.COM chunk->flags = UATH_CFLAGS_FINAL;
207610128SFei.Feng@Sun.COM chunk->length = BE_16(msglen);
207710128SFei.Feng@Sun.COM
207810128SFei.Feng@Sun.COM /* fill Tx descriptor */
207910128SFei.Feng@Sun.COM desc->msglen = BE_32(msglen);
208010128SFei.Feng@Sun.COM /* NB: to get UATH_TX_NOTIFY reply, `msgid' must be larger than 0 */
208110128SFei.Feng@Sun.COM desc->msgid = sc->sc_msgid; /* don't care about endianness */
208210128SFei.Feng@Sun.COM desc->type = BE_32(WDCMSG_SEND);
208310128SFei.Feng@Sun.COM switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
208410128SFei.Feng@Sun.COM case IEEE80211_FC0_TYPE_CTL:
208510128SFei.Feng@Sun.COM case IEEE80211_FC0_TYPE_MGT:
208610128SFei.Feng@Sun.COM /* NB: force all management frames to highest queue */
208710128SFei.Feng@Sun.COM if (ni->in_flags & UATH_NODE_QOS) {
208810128SFei.Feng@Sun.COM /* NB: force all management frames to highest queue */
208910128SFei.Feng@Sun.COM desc->txqid = BE_32(WME_AC_VO | UATH_TXQID_MINRATE);
209010128SFei.Feng@Sun.COM } else
209110128SFei.Feng@Sun.COM desc->txqid = BE_32(WME_AC_BE | UATH_TXQID_MINRATE);
209210128SFei.Feng@Sun.COM break;
209310128SFei.Feng@Sun.COM case IEEE80211_FC0_TYPE_DATA:
209410128SFei.Feng@Sun.COM /* XXX multicast frames should honor mcastrate */
209510128SFei.Feng@Sun.COM desc->txqid = BE_32(WME_AC_BE);
209610128SFei.Feng@Sun.COM break;
209710128SFei.Feng@Sun.COM default:
209810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_TX, "uath: uath_send(): "
209910128SFei.Feng@Sun.COM "bogus frame type 0x%x (%s)\n",
210010128SFei.Feng@Sun.COM wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
210110128SFei.Feng@Sun.COM err = EIO;
210210128SFei.Feng@Sun.COM goto fail;
210310128SFei.Feng@Sun.COM }
210410128SFei.Feng@Sun.COM
210510128SFei.Feng@Sun.COM if (ic->ic_state == IEEE80211_S_AUTH ||
210610128SFei.Feng@Sun.COM ic->ic_state == IEEE80211_S_ASSOC ||
210710128SFei.Feng@Sun.COM ic->ic_state == IEEE80211_S_RUN)
210810128SFei.Feng@Sun.COM desc->connid = BE_32(UATH_ID_BSS);
210910128SFei.Feng@Sun.COM else
211010128SFei.Feng@Sun.COM desc->connid = BE_32(UATH_ID_INVALID);
211110128SFei.Feng@Sun.COM desc->flags = BE_32(0 /* no UATH_TX_NOTIFY */);
211210128SFei.Feng@Sun.COM desc->buflen = BE_32(pktlen);
211310128SFei.Feng@Sun.COM
211410128SFei.Feng@Sun.COM (void) uath_tx_data_xfer(sc, m);
211510128SFei.Feng@Sun.COM
211610128SFei.Feng@Sun.COM sc->sc_msgid = (sc->sc_msgid + 1) % UATH_TX_DATA_LIST_COUNT;
211710128SFei.Feng@Sun.COM
211810128SFei.Feng@Sun.COM ic->ic_stats.is_tx_frags++;
211910128SFei.Feng@Sun.COM ic->ic_stats.is_tx_bytes += pktlen;
212010128SFei.Feng@Sun.COM
212110128SFei.Feng@Sun.COM fail:
212210128SFei.Feng@Sun.COM if (ni != NULL)
212310128SFei.Feng@Sun.COM ieee80211_free_node(ni);
212410128SFei.Feng@Sun.COM if ((mp) &&
212510128SFei.Feng@Sun.COM ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA ||
212610128SFei.Feng@Sun.COM err == 0)) {
212710128SFei.Feng@Sun.COM freemsg(mp);
212810128SFei.Feng@Sun.COM }
212910128SFei.Feng@Sun.COM mutex_exit(&sc->sc_txlock_data);
213010128SFei.Feng@Sun.COM return (err);
213110128SFei.Feng@Sun.COM }
213210128SFei.Feng@Sun.COM
213310128SFei.Feng@Sun.COM static int
uath_reconnect(dev_info_t * devinfo)213410128SFei.Feng@Sun.COM uath_reconnect(dev_info_t *devinfo)
213510128SFei.Feng@Sun.COM {
213610128SFei.Feng@Sun.COM struct uath_softc *sc;
213710128SFei.Feng@Sun.COM struct ieee80211com *ic;
213810128SFei.Feng@Sun.COM int err;
213910128SFei.Feng@Sun.COM uint16_t vendor_id, product_id;
214010128SFei.Feng@Sun.COM
214110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_reconnect(): "
214210128SFei.Feng@Sun.COM "uath online\n");
214310128SFei.Feng@Sun.COM
214410128SFei.Feng@Sun.COM sc = ddi_get_soft_state(uath_soft_state_p, ddi_get_instance(devinfo));
214510128SFei.Feng@Sun.COM ASSERT(sc != NULL);
214610128SFei.Feng@Sun.COM ic = (struct ieee80211com *)&sc->sc_ic;
214710128SFei.Feng@Sun.COM
214810128SFei.Feng@Sun.COM if (!UATH_IS_RECONNECT(sc)) {
214910128SFei.Feng@Sun.COM err = uath_open_pipes(sc);
215010128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
215110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
215210128SFei.Feng@Sun.COM "could not open pipes\n");
215310128SFei.Feng@Sun.COM return (DDI_FAILURE);
215410128SFei.Feng@Sun.COM }
215510128SFei.Feng@Sun.COM
215610128SFei.Feng@Sun.COM err = uath_loadfirmware(sc);
215710128SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
215810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
215910128SFei.Feng@Sun.COM "could not download firmware\n");
216010128SFei.Feng@Sun.COM return (DDI_FAILURE);
216110128SFei.Feng@Sun.COM }
216210128SFei.Feng@Sun.COM
216310128SFei.Feng@Sun.COM uath_close_pipes(sc);
216410128SFei.Feng@Sun.COM usb_client_detach(sc->sc_dev, sc->sc_udev);
216510128SFei.Feng@Sun.COM
216610128SFei.Feng@Sun.COM /* reset device */
216710128SFei.Feng@Sun.COM err = usb_reset_device(sc->sc_dev, USB_RESET_LVL_DEFAULT);
216810128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
216910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
217010128SFei.Feng@Sun.COM "could not reset device %x\n", err);
217110128SFei.Feng@Sun.COM }
217210128SFei.Feng@Sun.COM
217310128SFei.Feng@Sun.COM err = usb_client_attach(devinfo, USBDRV_VERSION, 0);
217410128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
217510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
217610128SFei.Feng@Sun.COM "usb_client_attach failed\n");
217710128SFei.Feng@Sun.COM }
217810128SFei.Feng@Sun.COM
217910128SFei.Feng@Sun.COM err = usb_get_dev_data(devinfo, &sc->sc_udev,
218010128SFei.Feng@Sun.COM USB_PARSE_LVL_ALL, 0);
218110128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
218210128SFei.Feng@Sun.COM sc->sc_udev = NULL;
218310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
218410128SFei.Feng@Sun.COM "usb_get_dev_data failed\n");
218510128SFei.Feng@Sun.COM }
218610128SFei.Feng@Sun.COM
218710128SFei.Feng@Sun.COM vendor_id = sc->sc_udev->dev_descr->idVendor;
218810128SFei.Feng@Sun.COM product_id = sc->sc_udev->dev_descr->idProduct;
218910128SFei.Feng@Sun.COM sc->dev_flags = uath_lookup(vendor_id, product_id);
219010128SFei.Feng@Sun.COM if (sc->dev_flags == UATH_FLAG_ERR) {
219110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
219210128SFei.Feng@Sun.COM "HW does not match\n");
219310128SFei.Feng@Sun.COM }
219410128SFei.Feng@Sun.COM
219510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_reconnect(): "
219610128SFei.Feng@Sun.COM "vendorId = %x,deviceID = %x, flags = %x\n",
219710128SFei.Feng@Sun.COM vendor_id, product_id, sc->dev_flags);
219810128SFei.Feng@Sun.COM
219910128SFei.Feng@Sun.COM UATH_LOCK(sc);
220010128SFei.Feng@Sun.COM sc->sc_flags |= UATH_FLAG_RECONNECT;
220110128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
220210128SFei.Feng@Sun.COM
220310128SFei.Feng@Sun.COM } else {
220410128SFei.Feng@Sun.COM err = uath_open_pipes(sc);
220510128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
220610128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
220710128SFei.Feng@Sun.COM "could not open pipes\n");
220810128SFei.Feng@Sun.COM return (DDI_FAILURE);
220910128SFei.Feng@Sun.COM }
221010128SFei.Feng@Sun.COM
221110128SFei.Feng@Sun.COM /*
221210128SFei.Feng@Sun.COM * Allocate xfers for firmware commands.
221310128SFei.Feng@Sun.COM */
221410128SFei.Feng@Sun.COM err = uath_alloc_cmd_list(sc, sc->sc_cmd, UATH_CMD_LIST_COUNT,
221510128SFei.Feng@Sun.COM UATH_MAX_CMDSZ);
221610128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
221710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
221810128SFei.Feng@Sun.COM "could not allocate Tx command list\n");
221910128SFei.Feng@Sun.COM return (DDI_FAILURE);
222010128SFei.Feng@Sun.COM }
222110128SFei.Feng@Sun.COM
222210128SFei.Feng@Sun.COM err = uath_init_cmd_list(sc);
222310128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
222410128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
222510128SFei.Feng@Sun.COM "could not init RX command list\n");
222610128SFei.Feng@Sun.COM return (DDI_FAILURE);
222710128SFei.Feng@Sun.COM }
222810128SFei.Feng@Sun.COM
222910128SFei.Feng@Sun.COM /*
223010128SFei.Feng@Sun.COM * We're now ready to send+receive firmware commands.
223110128SFei.Feng@Sun.COM */
223210128SFei.Feng@Sun.COM err = uath_host_available(sc);
223310128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
223410128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
223510128SFei.Feng@Sun.COM "could not initialize adapter\n");
223610128SFei.Feng@Sun.COM return (DDI_FAILURE);
223710128SFei.Feng@Sun.COM }
223810128SFei.Feng@Sun.COM
223910128SFei.Feng@Sun.COM err = uath_get_devcap(sc);
224010128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
224110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
224210128SFei.Feng@Sun.COM "could not get device capabilities\n");
224310128SFei.Feng@Sun.COM return (DDI_FAILURE);
224410128SFei.Feng@Sun.COM }
224510128SFei.Feng@Sun.COM
224610128SFei.Feng@Sun.COM err = uath_get_devstatus(sc, ic->ic_macaddr);
224710128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
224810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
224910128SFei.Feng@Sun.COM "could not get dev status\n");
225010128SFei.Feng@Sun.COM return (DDI_FAILURE);
225110128SFei.Feng@Sun.COM }
225210128SFei.Feng@Sun.COM
225310128SFei.Feng@Sun.COM err = usb_check_same_device(sc->sc_dev, NULL, USB_LOG_L2, -1,
225410128SFei.Feng@Sun.COM USB_CHK_BASIC, NULL);
225510128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
225610128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
225710128SFei.Feng@Sun.COM "different device connected %x\n", err);
225810128SFei.Feng@Sun.COM return (DDI_FAILURE);
225910128SFei.Feng@Sun.COM }
226010128SFei.Feng@Sun.COM
226110128SFei.Feng@Sun.COM err = uath_init(sc);
226210128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
226310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_reconnect(): "
226410128SFei.Feng@Sun.COM "device re-connect failed\n");
226510128SFei.Feng@Sun.COM return (DDI_FAILURE);
226610128SFei.Feng@Sun.COM }
226710128SFei.Feng@Sun.COM
226810128SFei.Feng@Sun.COM UATH_LOCK(sc);
226910128SFei.Feng@Sun.COM sc->sc_flags &= ~UATH_FLAG_RECONNECT;
227010128SFei.Feng@Sun.COM sc->sc_flags &= ~UATH_FLAG_DISCONNECT;
227110128SFei.Feng@Sun.COM sc->sc_flags |= UATH_FLAG_RUNNING;
227210128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
227310128SFei.Feng@Sun.COM }
227410128SFei.Feng@Sun.COM
227510128SFei.Feng@Sun.COM return (DDI_SUCCESS);
227610128SFei.Feng@Sun.COM }
227710128SFei.Feng@Sun.COM
227810128SFei.Feng@Sun.COM static int
uath_disconnect(dev_info_t * devinfo)227910128SFei.Feng@Sun.COM uath_disconnect(dev_info_t *devinfo)
228010128SFei.Feng@Sun.COM {
228110128SFei.Feng@Sun.COM struct uath_softc *sc;
228210128SFei.Feng@Sun.COM struct ieee80211com *ic;
228310128SFei.Feng@Sun.COM
228410128SFei.Feng@Sun.COM /*
228510128SFei.Feng@Sun.COM * We can't call uath_stop() here, since the hardware is removed,
228610128SFei.Feng@Sun.COM * we can't access the register anymore.
228710128SFei.Feng@Sun.COM */
228810128SFei.Feng@Sun.COM sc = ddi_get_soft_state(uath_soft_state_p, ddi_get_instance(devinfo));
228910128SFei.Feng@Sun.COM ASSERT(sc != NULL);
229010128SFei.Feng@Sun.COM
229110128SFei.Feng@Sun.COM if (sc->sc_flags & UATH_FLAG_RECONNECT) {
229210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_disconnect(): "
229310128SFei.Feng@Sun.COM "stage 0 in re-connect\n");
229410128SFei.Feng@Sun.COM uath_close_pipes(sc);
229510128SFei.Feng@Sun.COM return (DDI_SUCCESS);
229610128SFei.Feng@Sun.COM }
229710128SFei.Feng@Sun.COM
229810128SFei.Feng@Sun.COM UATH_LOCK(sc);
229910128SFei.Feng@Sun.COM sc->sc_flags |= UATH_FLAG_DISCONNECT;
230010128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
230110128SFei.Feng@Sun.COM
230210128SFei.Feng@Sun.COM ic = (struct ieee80211com *)&sc->sc_ic;
230310128SFei.Feng@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
230410128SFei.Feng@Sun.COM
230510128SFei.Feng@Sun.COM UATH_LOCK(sc);
230610128SFei.Feng@Sun.COM sc->sc_flags &= ~UATH_FLAG_RUNNING; /* STOP */
230710128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
230810128SFei.Feng@Sun.COM
230910128SFei.Feng@Sun.COM /* abort and free xfers */
231010128SFei.Feng@Sun.COM uath_free_cmd_list(sc->sc_cmd, UATH_CMD_LIST_COUNT);
231110128SFei.Feng@Sun.COM
231210128SFei.Feng@Sun.COM /* close Tx/Rx pipes */
231310128SFei.Feng@Sun.COM uath_close_pipes(sc);
231410128SFei.Feng@Sun.COM
231510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_disconnect(): "
231610128SFei.Feng@Sun.COM "offline success\n");
231710128SFei.Feng@Sun.COM
231810128SFei.Feng@Sun.COM return (DDI_SUCCESS);
231910128SFei.Feng@Sun.COM }
232010128SFei.Feng@Sun.COM
232110128SFei.Feng@Sun.COM static int
uath_dataflush(struct uath_softc * sc)232210128SFei.Feng@Sun.COM uath_dataflush(struct uath_softc *sc)
232310128SFei.Feng@Sun.COM {
232410128SFei.Feng@Sun.COM struct uath_chunk *chunk;
232510128SFei.Feng@Sun.COM struct uath_tx_desc *desc;
232610128SFei.Feng@Sun.COM uint8_t *buf;
232710128SFei.Feng@Sun.COM int err;
232810128SFei.Feng@Sun.COM
232910128SFei.Feng@Sun.COM buf = kmem_alloc(UATH_MAX_TXBUFSZ, KM_NOSLEEP);
233010128SFei.Feng@Sun.COM if (buf == NULL) {
233110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_dataflush(): "
233210128SFei.Feng@Sun.COM "no bufs\n");
233310128SFei.Feng@Sun.COM return (ENOBUFS);
233410128SFei.Feng@Sun.COM }
233510128SFei.Feng@Sun.COM
233610128SFei.Feng@Sun.COM chunk = (struct uath_chunk *)buf;
233710128SFei.Feng@Sun.COM desc = (struct uath_tx_desc *)(chunk + 1);
233810128SFei.Feng@Sun.COM
233910128SFei.Feng@Sun.COM /* one chunk only */
234010128SFei.Feng@Sun.COM chunk->seqnum = 0;
234110128SFei.Feng@Sun.COM chunk->flags = UATH_CFLAGS_FINAL;
234210128SFei.Feng@Sun.COM chunk->length = BE_16(sizeof (struct uath_tx_desc));
234310128SFei.Feng@Sun.COM
234410128SFei.Feng@Sun.COM bzero(desc, sizeof (struct uath_tx_desc));
234510128SFei.Feng@Sun.COM desc->msglen = BE_32(sizeof (struct uath_tx_desc));
234610128SFei.Feng@Sun.COM desc->msgid = sc->sc_msgid; /* don't care about endianness */
234710128SFei.Feng@Sun.COM desc->type = BE_32(WDCMSG_FLUSH);
234810128SFei.Feng@Sun.COM desc->txqid = BE_32(0);
234910128SFei.Feng@Sun.COM desc->connid = BE_32(0);
235010128SFei.Feng@Sun.COM desc->flags = BE_32(0);
235110128SFei.Feng@Sun.COM
235210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_dataflush(): "
235310128SFei.Feng@Sun.COM "send flush ix %d\n", desc->msgid);
235410128SFei.Feng@Sun.COM
235510128SFei.Feng@Sun.COM err = uath_fw_send(sc, sc->tx_data_pipe, buf,
235610128SFei.Feng@Sun.COM sizeof (struct uath_chunk) + sizeof (struct uath_tx_desc));
235710128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
235810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_dataflush(): "
235910128SFei.Feng@Sun.COM "data flush error");
236010128SFei.Feng@Sun.COM return (UATH_FAILURE);
236110128SFei.Feng@Sun.COM }
236210128SFei.Feng@Sun.COM
236310128SFei.Feng@Sun.COM kmem_free(buf, UATH_MAX_TXBUFSZ);
236410128SFei.Feng@Sun.COM sc->sc_msgid = (sc->sc_msgid + 1) % UATH_TX_DATA_LIST_COUNT;
236510128SFei.Feng@Sun.COM
236610128SFei.Feng@Sun.COM return (UATH_SUCCESS);
236710128SFei.Feng@Sun.COM }
236810128SFei.Feng@Sun.COM
236910128SFei.Feng@Sun.COM static int
uath_cmdflush(struct uath_softc * sc)237010128SFei.Feng@Sun.COM uath_cmdflush(struct uath_softc *sc)
237110128SFei.Feng@Sun.COM {
237210128SFei.Feng@Sun.COM return (uath_cmd_write(sc, WDCMSG_FLUSH, NULL, 0, 0));
237310128SFei.Feng@Sun.COM }
237410128SFei.Feng@Sun.COM
237510128SFei.Feng@Sun.COM static int
uath_flush(struct uath_softc * sc)237610128SFei.Feng@Sun.COM uath_flush(struct uath_softc *sc)
237710128SFei.Feng@Sun.COM {
237810128SFei.Feng@Sun.COM int err;
237910128SFei.Feng@Sun.COM
238010128SFei.Feng@Sun.COM err = uath_dataflush(sc);
238110128SFei.Feng@Sun.COM if (err != UATH_SUCCESS)
238210128SFei.Feng@Sun.COM goto failed;
238310128SFei.Feng@Sun.COM
238410128SFei.Feng@Sun.COM err = uath_cmdflush(sc);
238510128SFei.Feng@Sun.COM if (err != UATH_SUCCESS)
238610128SFei.Feng@Sun.COM goto failed;
238710128SFei.Feng@Sun.COM
238810128SFei.Feng@Sun.COM return (UATH_SUCCESS);
238910128SFei.Feng@Sun.COM failed:
239010128SFei.Feng@Sun.COM return (err);
239110128SFei.Feng@Sun.COM }
239210128SFei.Feng@Sun.COM
239310128SFei.Feng@Sun.COM static int
uath_set_ledstate(struct uath_softc * sc,int connected)239410128SFei.Feng@Sun.COM uath_set_ledstate(struct uath_softc *sc, int connected)
239510128SFei.Feng@Sun.COM {
239610128SFei.Feng@Sun.COM int err;
239710128SFei.Feng@Sun.COM
239810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_set_ledstate(): "
239910128SFei.Feng@Sun.COM "set led state %sconnected\n", connected ? "" : "!");
240010128SFei.Feng@Sun.COM
240110128SFei.Feng@Sun.COM connected = BE_32(connected);
240210128SFei.Feng@Sun.COM err = uath_cmd_write(sc, WDCMSG_SET_LED_STATE,
240310128SFei.Feng@Sun.COM &connected, sizeof (connected), 0);
240410128SFei.Feng@Sun.COM return (err);
240510128SFei.Feng@Sun.COM }
240610128SFei.Feng@Sun.COM
240710128SFei.Feng@Sun.COM static int
uath_config_multi(struct uath_softc * sc,uint32_t reg,const void * data,int len)240810128SFei.Feng@Sun.COM uath_config_multi(struct uath_softc *sc, uint32_t reg, const void *data,
240910128SFei.Feng@Sun.COM int len)
241010128SFei.Feng@Sun.COM {
241110128SFei.Feng@Sun.COM struct uath_write_mac write;
241210128SFei.Feng@Sun.COM int err;
241310128SFei.Feng@Sun.COM
241410128SFei.Feng@Sun.COM write.reg = BE_32(reg);
241510128SFei.Feng@Sun.COM write.len = BE_32(len);
241610128SFei.Feng@Sun.COM bcopy(data, write.data, len);
241710128SFei.Feng@Sun.COM
241810128SFei.Feng@Sun.COM /* properly handle the case where len is zero (reset) */
241910128SFei.Feng@Sun.COM err = uath_cmd_write(sc, WDCMSG_TARGET_SET_CONFIG, &write,
242010128SFei.Feng@Sun.COM (len == 0) ? sizeof (uint32_t) : 2 * sizeof (uint32_t) + len, 0);
242110128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
242210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_config_multi(): "
242310128SFei.Feng@Sun.COM "could not write %d bytes to register 0x%02x\n", len, reg);
242410128SFei.Feng@Sun.COM }
242510128SFei.Feng@Sun.COM return (err);
242610128SFei.Feng@Sun.COM }
242710128SFei.Feng@Sun.COM
242810128SFei.Feng@Sun.COM static void
uath_config(struct uath_softc * sc,uint32_t reg,uint32_t val)242910128SFei.Feng@Sun.COM uath_config(struct uath_softc *sc, uint32_t reg, uint32_t val)
243010128SFei.Feng@Sun.COM {
243110128SFei.Feng@Sun.COM struct uath_write_mac write;
243210128SFei.Feng@Sun.COM int err;
243310128SFei.Feng@Sun.COM
243410128SFei.Feng@Sun.COM write.reg = BE_32(reg);
243510128SFei.Feng@Sun.COM write.len = BE_32(0); /* 0 = single write */
243610128SFei.Feng@Sun.COM *(uint32_t *)write.data = BE_32(val);
243710128SFei.Feng@Sun.COM
243810128SFei.Feng@Sun.COM err = uath_cmd_write(sc, WDCMSG_TARGET_SET_CONFIG, &write,
243910128SFei.Feng@Sun.COM 3 * sizeof (uint32_t), 0);
244010128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
244110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_config(): "
244210128SFei.Feng@Sun.COM "could not write register 0x%02x\n",
244310128SFei.Feng@Sun.COM reg);
244410128SFei.Feng@Sun.COM }
244510128SFei.Feng@Sun.COM }
244610128SFei.Feng@Sun.COM
244710128SFei.Feng@Sun.COM static int
uath_switch_channel(struct uath_softc * sc,struct ieee80211_channel * c)244810128SFei.Feng@Sun.COM uath_switch_channel(struct uath_softc *sc, struct ieee80211_channel *c)
244910128SFei.Feng@Sun.COM {
245010128SFei.Feng@Sun.COM int err;
245110128SFei.Feng@Sun.COM
245210128SFei.Feng@Sun.COM /* set radio frequency */
245310128SFei.Feng@Sun.COM err = uath_set_chan(sc, c);
245410128SFei.Feng@Sun.COM if (err) {
245510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_switch_channel(): "
245610128SFei.Feng@Sun.COM "could not set channel\n");
245710128SFei.Feng@Sun.COM goto failed;
245810128SFei.Feng@Sun.COM }
245910128SFei.Feng@Sun.COM
246010128SFei.Feng@Sun.COM /* reset Tx rings */
246110128SFei.Feng@Sun.COM err = uath_reset_tx_queues(sc);
246210128SFei.Feng@Sun.COM if (err) {
246310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_switch_channel(): "
246410128SFei.Feng@Sun.COM "could not reset Tx queues\n");
246510128SFei.Feng@Sun.COM goto failed;
246610128SFei.Feng@Sun.COM }
246710128SFei.Feng@Sun.COM
246810128SFei.Feng@Sun.COM /* set Tx rings WME properties */
246910128SFei.Feng@Sun.COM err = uath_wme_init(sc);
247010128SFei.Feng@Sun.COM if (err) {
247110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_switch_channel(): "
247210128SFei.Feng@Sun.COM "could not init Tx queues\n");
247310128SFei.Feng@Sun.COM goto failed;
247410128SFei.Feng@Sun.COM }
247510128SFei.Feng@Sun.COM
247610128SFei.Feng@Sun.COM err = uath_set_ledstate(sc, 0);
247710128SFei.Feng@Sun.COM if (err) {
247810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_switch_channel(): "
247910128SFei.Feng@Sun.COM "could not set led state\n");
248010128SFei.Feng@Sun.COM goto failed;
248110128SFei.Feng@Sun.COM }
248210128SFei.Feng@Sun.COM
248310128SFei.Feng@Sun.COM err = uath_flush(sc);
248410128SFei.Feng@Sun.COM if (err) {
248510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_switch_channel(): "
248610128SFei.Feng@Sun.COM "could not flush pipes\n");
248710128SFei.Feng@Sun.COM goto failed;
248810128SFei.Feng@Sun.COM }
248910128SFei.Feng@Sun.COM
249010128SFei.Feng@Sun.COM failed:
249110128SFei.Feng@Sun.COM return (err);
249210128SFei.Feng@Sun.COM }
249310128SFei.Feng@Sun.COM
249410128SFei.Feng@Sun.COM static int
uath_set_rxfilter(struct uath_softc * sc,uint32_t bits,uint32_t op)249510128SFei.Feng@Sun.COM uath_set_rxfilter(struct uath_softc *sc, uint32_t bits, uint32_t op)
249610128SFei.Feng@Sun.COM {
249710128SFei.Feng@Sun.COM struct uath_cmd_rx_filter rxfilter;
249810128SFei.Feng@Sun.COM
249910128SFei.Feng@Sun.COM rxfilter.bits = BE_32(bits);
250010128SFei.Feng@Sun.COM rxfilter.op = BE_32(op);
250110128SFei.Feng@Sun.COM
250210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_set_rxfilter(): "
250310128SFei.Feng@Sun.COM "setting Rx filter=0x%x flags=0x%x\n", bits, op);
250410128SFei.Feng@Sun.COM
250510128SFei.Feng@Sun.COM return ((uath_cmd_write(sc, WDCMSG_RX_FILTER, &rxfilter,
250610128SFei.Feng@Sun.COM sizeof (rxfilter), 0)));
250710128SFei.Feng@Sun.COM }
250810128SFei.Feng@Sun.COM
250910128SFei.Feng@Sun.COM static int
uath_set_chan(struct uath_softc * sc,struct ieee80211_channel * c)251010128SFei.Feng@Sun.COM uath_set_chan(struct uath_softc *sc, struct ieee80211_channel *c)
251110128SFei.Feng@Sun.COM {
251210128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
251310128SFei.Feng@Sun.COM struct uath_cmd_reset reset;
251410128SFei.Feng@Sun.COM
251510128SFei.Feng@Sun.COM bzero(&reset, sizeof (reset));
251610128SFei.Feng@Sun.COM if (IEEE80211_IS_CHAN_2GHZ(c))
251710128SFei.Feng@Sun.COM reset.flags |= BE_32(UATH_CHAN_2GHZ);
251810128SFei.Feng@Sun.COM if (IEEE80211_IS_CHAN_5GHZ(c))
251910128SFei.Feng@Sun.COM reset.flags |= BE_32(UATH_CHAN_5GHZ);
252010128SFei.Feng@Sun.COM /* NB: 11g =>'s 11b so don't specify both OFDM and CCK */
252110128SFei.Feng@Sun.COM if (UATH_IS_CHAN_OFDM(c))
252210128SFei.Feng@Sun.COM reset.flags |= BE_32(UATH_CHAN_OFDM);
252310128SFei.Feng@Sun.COM else if (UATH_IS_CHAN_CCK(c))
252410128SFei.Feng@Sun.COM reset.flags |= BE_32(UATH_CHAN_CCK);
252510128SFei.Feng@Sun.COM /* turbo can be used in either 2GHz or 5GHz */
252610128SFei.Feng@Sun.COM if (c->ich_flags & IEEE80211_CHAN_TURBO)
252710128SFei.Feng@Sun.COM reset.flags |= BE_32(UATH_CHAN_TURBO);
252810128SFei.Feng@Sun.COM
252910128SFei.Feng@Sun.COM reset.freq = BE_32(c->ich_freq);
253010128SFei.Feng@Sun.COM reset.maxrdpower = BE_32(50); /* XXX */
253110128SFei.Feng@Sun.COM reset.channelchange = BE_32(1);
253210128SFei.Feng@Sun.COM reset.keeprccontent = BE_32(0);
253310128SFei.Feng@Sun.COM
253410128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_set_chan(): "
253510128SFei.Feng@Sun.COM "set channel %d, flags 0x%x freq %u\n",
253610128SFei.Feng@Sun.COM ieee80211_chan2ieee(ic, c),
253710128SFei.Feng@Sun.COM BE_32(reset.flags), BE_32(reset.freq));
253810128SFei.Feng@Sun.COM
253910128SFei.Feng@Sun.COM return (uath_cmd_write(sc, WDCMSG_RESET, &reset, sizeof (reset), 0));
254010128SFei.Feng@Sun.COM }
254110128SFei.Feng@Sun.COM
254210128SFei.Feng@Sun.COM static int
uath_reset_tx_queues(struct uath_softc * sc)254310128SFei.Feng@Sun.COM uath_reset_tx_queues(struct uath_softc *sc)
254410128SFei.Feng@Sun.COM {
254510128SFei.Feng@Sun.COM int ac, err;
254610128SFei.Feng@Sun.COM
254710128SFei.Feng@Sun.COM for (ac = 0; ac < 4; ac++) {
254810128SFei.Feng@Sun.COM const uint32_t qid = BE_32(ac);
254910128SFei.Feng@Sun.COM err = uath_cmd_write(sc, WDCMSG_RELEASE_TX_QUEUE, &qid,
255010128SFei.Feng@Sun.COM sizeof (qid), 0);
255110128SFei.Feng@Sun.COM if (err != UATH_SUCCESS)
255210128SFei.Feng@Sun.COM break;
255310128SFei.Feng@Sun.COM }
255410128SFei.Feng@Sun.COM return (err);
255510128SFei.Feng@Sun.COM }
255610128SFei.Feng@Sun.COM
255710128SFei.Feng@Sun.COM static int
uath_wme_init(struct uath_softc * sc)255810128SFei.Feng@Sun.COM uath_wme_init(struct uath_softc *sc)
255910128SFei.Feng@Sun.COM {
256010128SFei.Feng@Sun.COM /* XXX get from net80211 */
256110128SFei.Feng@Sun.COM static const struct uath_wme_settings uath_wme_11g[4] = {
256210128SFei.Feng@Sun.COM { 7, 4, 10, 0, 0 }, /* Background */
256310128SFei.Feng@Sun.COM { 3, 4, 10, 0, 0 }, /* Best-Effort */
256410128SFei.Feng@Sun.COM { 3, 3, 4, 26, 0 }, /* Video */
256510128SFei.Feng@Sun.COM { 2, 2, 3, 47, 0 } /* Voice */
256610128SFei.Feng@Sun.COM };
256710128SFei.Feng@Sun.COM
256810128SFei.Feng@Sun.COM struct uath_cmd_txq_setup qinfo;
256910128SFei.Feng@Sun.COM int ac, err;
257010128SFei.Feng@Sun.COM
257110128SFei.Feng@Sun.COM for (ac = 0; ac < 4; ac++) {
257210128SFei.Feng@Sun.COM qinfo.qid = BE_32(ac);
257310128SFei.Feng@Sun.COM qinfo.len = BE_32(sizeof (qinfo.attr));
257410128SFei.Feng@Sun.COM qinfo.attr.priority = BE_32(ac); /* XXX */
257510128SFei.Feng@Sun.COM qinfo.attr.aifs = BE_32(uath_wme_11g[ac].aifsn);
257610128SFei.Feng@Sun.COM qinfo.attr.logcwmin = BE_32(uath_wme_11g[ac].logcwmin);
257710128SFei.Feng@Sun.COM qinfo.attr.logcwmax = BE_32(uath_wme_11g[ac].logcwmax);
257810128SFei.Feng@Sun.COM qinfo.attr.mode = BE_32(uath_wme_11g[ac].acm);
257910128SFei.Feng@Sun.COM qinfo.attr.qflags = BE_32(1);
258010128SFei.Feng@Sun.COM qinfo.attr.bursttime =
258110128SFei.Feng@Sun.COM BE_32(UATH_TXOP_TO_US(uath_wme_11g[ac].txop));
258210128SFei.Feng@Sun.COM
258310128SFei.Feng@Sun.COM err = uath_cmd_write(sc, WDCMSG_SETUP_TX_QUEUE, &qinfo,
258410128SFei.Feng@Sun.COM sizeof (qinfo), 0);
258510128SFei.Feng@Sun.COM if (err != UATH_SUCCESS)
258610128SFei.Feng@Sun.COM break;
258710128SFei.Feng@Sun.COM }
258810128SFei.Feng@Sun.COM return (err);
258910128SFei.Feng@Sun.COM }
259010128SFei.Feng@Sun.COM
259110128SFei.Feng@Sun.COM static void
uath_stop_locked(void * arg)259210128SFei.Feng@Sun.COM uath_stop_locked(void *arg)
259310128SFei.Feng@Sun.COM {
259410128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)arg;
259510128SFei.Feng@Sun.COM
259610128SFei.Feng@Sun.COM /* flush data & control requests into the target */
259710128SFei.Feng@Sun.COM (void) uath_flush(sc);
259810128SFei.Feng@Sun.COM
259910128SFei.Feng@Sun.COM /* set a LED status to the disconnected. */
260010128SFei.Feng@Sun.COM (void) uath_set_ledstate(sc, 0);
260110128SFei.Feng@Sun.COM
260210128SFei.Feng@Sun.COM /* stop the target */
260310128SFei.Feng@Sun.COM (void) uath_cmd_write(sc, WDCMSG_TARGET_STOP, NULL, 0, 0);
260410128SFei.Feng@Sun.COM
260510128SFei.Feng@Sun.COM /* abort any pending transfers */
260610128SFei.Feng@Sun.COM usb_pipe_reset(sc->sc_dev, sc->rx_data_pipe, USB_FLAGS_SLEEP, NULL, 0);
260710128SFei.Feng@Sun.COM usb_pipe_reset(sc->sc_dev, sc->tx_data_pipe, USB_FLAGS_SLEEP, NULL, 0);
260810128SFei.Feng@Sun.COM usb_pipe_reset(sc->sc_dev, sc->tx_cmd_pipe, USB_FLAGS_SLEEP, NULL, 0);
260910128SFei.Feng@Sun.COM }
261010128SFei.Feng@Sun.COM
261110128SFei.Feng@Sun.COM static int
uath_init_locked(void * arg)261210128SFei.Feng@Sun.COM uath_init_locked(void *arg)
261310128SFei.Feng@Sun.COM {
261410128SFei.Feng@Sun.COM struct uath_softc *sc = arg;
261510128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
261610128SFei.Feng@Sun.COM uint32_t val;
261710128SFei.Feng@Sun.COM int i, err;
261810128SFei.Feng@Sun.COM
261910128SFei.Feng@Sun.COM if (UATH_IS_RUNNING(sc))
262010128SFei.Feng@Sun.COM uath_stop_locked(sc);
262110128SFei.Feng@Sun.COM
262210128SFei.Feng@Sun.COM uath_init_data_queue(sc);
262310128SFei.Feng@Sun.COM
262410128SFei.Feng@Sun.COM /* reset variables */
262510128SFei.Feng@Sun.COM sc->sc_intrx_nextnum = sc->sc_msgid = 0;
262610128SFei.Feng@Sun.COM
262710128SFei.Feng@Sun.COM val = BE_32(0);
262810128SFei.Feng@Sun.COM (void) uath_cmd_write(sc, WDCMSG_BIND, &val, sizeof (val), 0);
262910128SFei.Feng@Sun.COM
263010128SFei.Feng@Sun.COM /* set MAC address */
263110128SFei.Feng@Sun.COM (void) uath_config_multi(sc, CFG_MAC_ADDR,
263210128SFei.Feng@Sun.COM ic->ic_macaddr, IEEE80211_ADDR_LEN);
263310128SFei.Feng@Sun.COM
263410128SFei.Feng@Sun.COM /* XXX honor net80211 state */
263510128SFei.Feng@Sun.COM uath_config(sc, CFG_RATE_CONTROL_ENABLE, 0x00000001);
263610128SFei.Feng@Sun.COM uath_config(sc, CFG_DIVERSITY_CTL, 0x00000001);
263710128SFei.Feng@Sun.COM uath_config(sc, CFG_ABOLT, 0x0000003f);
263810128SFei.Feng@Sun.COM uath_config(sc, CFG_WME_ENABLED, 0x00000001);
263910128SFei.Feng@Sun.COM
264010128SFei.Feng@Sun.COM uath_config(sc, CFG_SERVICE_TYPE, 1);
264110128SFei.Feng@Sun.COM uath_config(sc, CFG_TP_SCALE, 0x00000000);
264210128SFei.Feng@Sun.COM uath_config(sc, CFG_TPC_HALF_DBM5, 0x0000003c);
264310128SFei.Feng@Sun.COM uath_config(sc, CFG_TPC_HALF_DBM2, 0x0000003c);
264410128SFei.Feng@Sun.COM uath_config(sc, CFG_OVERRD_TX_POWER, 0x00000000);
264510128SFei.Feng@Sun.COM uath_config(sc, CFG_GMODE_PROTECTION, 0x00000000);
264610128SFei.Feng@Sun.COM uath_config(sc, CFG_GMODE_PROTECT_RATE_INDEX, 0x00000003);
264710128SFei.Feng@Sun.COM uath_config(sc, CFG_PROTECTION_TYPE, 0x00000000);
264810128SFei.Feng@Sun.COM uath_config(sc, CFG_MODE_CTS, 0x00000002);
264910128SFei.Feng@Sun.COM
265010128SFei.Feng@Sun.COM err = uath_cmd_read(sc, WDCMSG_TARGET_START, NULL, 0,
265110128SFei.Feng@Sun.COM &val, sizeof (val), UATH_CMD_FLAG_MAGIC);
265210128SFei.Feng@Sun.COM if (err) {
265310128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_init_locked(): "
265410128SFei.Feng@Sun.COM "could not start target\n");
265510128SFei.Feng@Sun.COM goto fail;
265610128SFei.Feng@Sun.COM }
265710128SFei.Feng@Sun.COM
265810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_init_locked(): "
265910128SFei.Feng@Sun.COM "%s returns handle: 0x%x\n",
266010128SFei.Feng@Sun.COM uath_codename(WDCMSG_TARGET_START), BE_32(val));
266110128SFei.Feng@Sun.COM
266210128SFei.Feng@Sun.COM /* set default channel */
266310128SFei.Feng@Sun.COM err = uath_switch_channel(sc, ic->ic_curchan);
266410128SFei.Feng@Sun.COM if (err) {
266510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_init_locked(): "
266610128SFei.Feng@Sun.COM "could not switch channel, error %d\n", err);
266710128SFei.Feng@Sun.COM goto fail;
266810128SFei.Feng@Sun.COM }
266910128SFei.Feng@Sun.COM
267010128SFei.Feng@Sun.COM val = BE_32(TARGET_DEVICE_AWAKE);
267110128SFei.Feng@Sun.COM (void) uath_cmd_write(sc, WDCMSG_SET_PWR_MODE, &val, sizeof (val), 0);
267210128SFei.Feng@Sun.COM /* XXX? check */
267310128SFei.Feng@Sun.COM (void) uath_cmd_write(sc, WDCMSG_RESET_KEY_CACHE, NULL, 0, 0);
267410128SFei.Feng@Sun.COM
267510128SFei.Feng@Sun.COM for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) {
267610128SFei.Feng@Sun.COM err = uath_rx_data_xfer(sc);
267710128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
267810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_init_locked(): "
267910128SFei.Feng@Sun.COM "could not alloc rx xfer %x\n", i);
268010128SFei.Feng@Sun.COM goto fail;
268110128SFei.Feng@Sun.COM }
268210128SFei.Feng@Sun.COM }
268310128SFei.Feng@Sun.COM
268410128SFei.Feng@Sun.COM /* enable Rx */
268510128SFei.Feng@Sun.COM (void) uath_set_rxfilter(sc, 0x0, UATH_FILTER_OP_INIT);
268610128SFei.Feng@Sun.COM (void) uath_set_rxfilter(sc,
268710128SFei.Feng@Sun.COM UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST |
268810128SFei.Feng@Sun.COM UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON,
268910128SFei.Feng@Sun.COM UATH_FILTER_OP_SET);
269010128SFei.Feng@Sun.COM
269110128SFei.Feng@Sun.COM return (UATH_SUCCESS);
269210128SFei.Feng@Sun.COM
269310128SFei.Feng@Sun.COM fail:
269410128SFei.Feng@Sun.COM uath_stop_locked(sc);
269510128SFei.Feng@Sun.COM return (err);
269610128SFei.Feng@Sun.COM }
269710128SFei.Feng@Sun.COM
269810128SFei.Feng@Sun.COM static int
uath_init(struct uath_softc * sc)269910128SFei.Feng@Sun.COM uath_init(struct uath_softc *sc)
270010128SFei.Feng@Sun.COM {
270110128SFei.Feng@Sun.COM int err;
270210128SFei.Feng@Sun.COM
270310128SFei.Feng@Sun.COM UATH_LOCK(sc);
270410128SFei.Feng@Sun.COM err = uath_init_locked(sc);
270510128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
270610128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_init(): "
270710128SFei.Feng@Sun.COM "failed to initialize uath hardware\n");
270810128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
270910128SFei.Feng@Sun.COM return (DDI_FAILURE);
271010128SFei.Feng@Sun.COM }
271110128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
271210128SFei.Feng@Sun.COM return (DDI_SUCCESS);
271310128SFei.Feng@Sun.COM }
271410128SFei.Feng@Sun.COM
271510128SFei.Feng@Sun.COM static void
uath_stop(struct uath_softc * sc)271610128SFei.Feng@Sun.COM uath_stop(struct uath_softc *sc)
271710128SFei.Feng@Sun.COM {
271810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_stop(): "
271910128SFei.Feng@Sun.COM "uath stop now\n");
272010128SFei.Feng@Sun.COM
272110128SFei.Feng@Sun.COM UATH_LOCK(sc);
272210128SFei.Feng@Sun.COM uath_stop_locked(sc);
272310128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
272410128SFei.Feng@Sun.COM }
272510128SFei.Feng@Sun.COM
272610128SFei.Feng@Sun.COM static void
uath_resume(struct uath_softc * sc)272710128SFei.Feng@Sun.COM uath_resume(struct uath_softc *sc)
272810128SFei.Feng@Sun.COM {
272910128SFei.Feng@Sun.COM int err;
273010128SFei.Feng@Sun.COM
273110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_resume(): "
273210128SFei.Feng@Sun.COM "uath resume now\n");
273310128SFei.Feng@Sun.COM
273410128SFei.Feng@Sun.COM /* check device changes after suspend */
273510128SFei.Feng@Sun.COM if (usb_check_same_device(sc->sc_dev, NULL, USB_LOG_L2, -1,
273610128SFei.Feng@Sun.COM USB_CHK_BASIC | USB_CHK_CFG, NULL) != USB_SUCCESS) {
273710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_resume: "
273810128SFei.Feng@Sun.COM "no or different device connected\n");
273910128SFei.Feng@Sun.COM return;
274010128SFei.Feng@Sun.COM }
274110128SFei.Feng@Sun.COM
274210128SFei.Feng@Sun.COM /*
274310128SFei.Feng@Sun.COM * initialize hardware
274410128SFei.Feng@Sun.COM */
274510128SFei.Feng@Sun.COM err = uath_init_cmd_list(sc);
274610128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
274710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_resume(): "
274810128SFei.Feng@Sun.COM "could not init RX command list\n");
274910128SFei.Feng@Sun.COM return;
275010128SFei.Feng@Sun.COM }
275110128SFei.Feng@Sun.COM
275210128SFei.Feng@Sun.COM err = uath_init(sc);
275310128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
275410128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_resume(): "
275510128SFei.Feng@Sun.COM "hardware init failed\n");
275610128SFei.Feng@Sun.COM uath_stop(sc);
275710128SFei.Feng@Sun.COM return;
275810128SFei.Feng@Sun.COM }
275910128SFei.Feng@Sun.COM
276010128SFei.Feng@Sun.COM ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
276110128SFei.Feng@Sun.COM UATH_LOCK(sc);
276210128SFei.Feng@Sun.COM sc->sc_flags &= ~UATH_FLAG_SUSPEND;
276310128SFei.Feng@Sun.COM sc->sc_flags |= UATH_FLAG_RUNNING;
276410128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
276510128SFei.Feng@Sun.COM }
276610128SFei.Feng@Sun.COM
276710128SFei.Feng@Sun.COM static int
uath_m_start(void * arg)276810128SFei.Feng@Sun.COM uath_m_start(void *arg)
276910128SFei.Feng@Sun.COM {
277010128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)arg;
277110128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
277210128SFei.Feng@Sun.COM int err;
277310128SFei.Feng@Sun.COM
277410128SFei.Feng@Sun.COM /*
277510128SFei.Feng@Sun.COM * initialize hardware
277610128SFei.Feng@Sun.COM */
277710128SFei.Feng@Sun.COM err = uath_init(sc);
277810128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
277910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_m_start(): "
278010128SFei.Feng@Sun.COM "device configuration failed\n");
278110128SFei.Feng@Sun.COM uath_stop(sc);
278210128SFei.Feng@Sun.COM return (err);
278310128SFei.Feng@Sun.COM }
278410128SFei.Feng@Sun.COM
278510128SFei.Feng@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
278610128SFei.Feng@Sun.COM
278710128SFei.Feng@Sun.COM UATH_LOCK(sc);
278810128SFei.Feng@Sun.COM sc->sc_flags |= UATH_FLAG_RUNNING;
278910128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
279010128SFei.Feng@Sun.COM return (DDI_SUCCESS);
279110128SFei.Feng@Sun.COM }
279210128SFei.Feng@Sun.COM
279310128SFei.Feng@Sun.COM static void
uath_m_stop(void * arg)279410128SFei.Feng@Sun.COM uath_m_stop(void *arg)
279510128SFei.Feng@Sun.COM {
279610128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)arg;
279710128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
279810128SFei.Feng@Sun.COM
279910128SFei.Feng@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
280010128SFei.Feng@Sun.COM
280110128SFei.Feng@Sun.COM if (!UATH_IS_DISCONNECT(sc))
280210128SFei.Feng@Sun.COM uath_stop(sc);
280310128SFei.Feng@Sun.COM
280410128SFei.Feng@Sun.COM UATH_LOCK(sc);
280510128SFei.Feng@Sun.COM sc->sc_flags &= ~UATH_FLAG_RUNNING;
280610128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
280710128SFei.Feng@Sun.COM }
280810128SFei.Feng@Sun.COM
280910128SFei.Feng@Sun.COM static void
uath_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)281010128SFei.Feng@Sun.COM uath_m_ioctl(void* arg, queue_t *wq, mblk_t *mp)
281110128SFei.Feng@Sun.COM {
281210128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)arg;
281310128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
281410128SFei.Feng@Sun.COM int err;
281510128SFei.Feng@Sun.COM
281610128SFei.Feng@Sun.COM err = ieee80211_ioctl(ic, wq, mp);
281710128SFei.Feng@Sun.COM UATH_LOCK(sc);
281810128SFei.Feng@Sun.COM if (err == ENETRESET) {
281910128SFei.Feng@Sun.COM if (ic->ic_des_esslen) {
282010128SFei.Feng@Sun.COM if (UATH_IS_RUNNING(sc)) {
282110128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
282210128SFei.Feng@Sun.COM (void) uath_init(sc);
282310128SFei.Feng@Sun.COM (void) ieee80211_new_state(ic,
282410128SFei.Feng@Sun.COM IEEE80211_S_SCAN, -1);
282510128SFei.Feng@Sun.COM UATH_LOCK(sc);
282610128SFei.Feng@Sun.COM }
282710128SFei.Feng@Sun.COM }
282810128SFei.Feng@Sun.COM }
282910128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
283010128SFei.Feng@Sun.COM }
283110128SFei.Feng@Sun.COM
283210128SFei.Feng@Sun.COM /*ARGSUSED*/
283310128SFei.Feng@Sun.COM static int
uath_m_unicst(void * arg,const uint8_t * macaddr)283410128SFei.Feng@Sun.COM uath_m_unicst(void *arg, const uint8_t *macaddr)
283510128SFei.Feng@Sun.COM {
283610128SFei.Feng@Sun.COM return (0);
283710128SFei.Feng@Sun.COM }
283810128SFei.Feng@Sun.COM
283910128SFei.Feng@Sun.COM /*ARGSUSED*/
284010128SFei.Feng@Sun.COM static int
uath_m_multicst(void * arg,boolean_t add,const uint8_t * mca)284110128SFei.Feng@Sun.COM uath_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
284210128SFei.Feng@Sun.COM {
284310128SFei.Feng@Sun.COM return (0);
284410128SFei.Feng@Sun.COM }
284510128SFei.Feng@Sun.COM
284610128SFei.Feng@Sun.COM /*ARGSUSED*/
284710128SFei.Feng@Sun.COM static int
uath_m_promisc(void * arg,boolean_t on)284810128SFei.Feng@Sun.COM uath_m_promisc(void *arg, boolean_t on)
284910128SFei.Feng@Sun.COM {
285010128SFei.Feng@Sun.COM return (0);
285110128SFei.Feng@Sun.COM }
285210128SFei.Feng@Sun.COM
285310128SFei.Feng@Sun.COM /*
285410128SFei.Feng@Sun.COM * callback functions for /get/set properties
285510128SFei.Feng@Sun.COM */
285610128SFei.Feng@Sun.COM static int
uath_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)285710128SFei.Feng@Sun.COM uath_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
285810128SFei.Feng@Sun.COM uint_t wldp_length, const void *wldp_buf)
285910128SFei.Feng@Sun.COM {
286010128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)arg;
286110128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
286210128SFei.Feng@Sun.COM int err;
286310128SFei.Feng@Sun.COM
286410128SFei.Feng@Sun.COM err = ieee80211_setprop(ic, pr_name, wldp_pr_num,
286510128SFei.Feng@Sun.COM wldp_length, wldp_buf);
286610128SFei.Feng@Sun.COM UATH_LOCK(sc);
286710128SFei.Feng@Sun.COM if (err == ENETRESET) {
286810128SFei.Feng@Sun.COM if (ic->ic_des_esslen && UATH_IS_RUNNING(sc)) {
286910128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
287010128SFei.Feng@Sun.COM (void) uath_init(sc);
287110128SFei.Feng@Sun.COM (void) ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
287210128SFei.Feng@Sun.COM UATH_LOCK(sc);
287310128SFei.Feng@Sun.COM }
287410128SFei.Feng@Sun.COM err = 0;
287510128SFei.Feng@Sun.COM }
287610128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
287710128SFei.Feng@Sun.COM return (err);
287810128SFei.Feng@Sun.COM }
287910128SFei.Feng@Sun.COM
288010128SFei.Feng@Sun.COM static int
uath_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)288110128SFei.Feng@Sun.COM uath_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2882*11878SVenu.Iyer@Sun.COM uint_t wldp_length, void *wldp_buf)
288310128SFei.Feng@Sun.COM {
288410128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)arg;
288510128SFei.Feng@Sun.COM int err;
288610128SFei.Feng@Sun.COM
288710128SFei.Feng@Sun.COM err = ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num,
2888*11878SVenu.Iyer@Sun.COM wldp_length, wldp_buf);
288910128SFei.Feng@Sun.COM return (err);
289010128SFei.Feng@Sun.COM }
289110128SFei.Feng@Sun.COM
2892*11878SVenu.Iyer@Sun.COM static void
uath_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,mac_prop_info_handle_t prh)2893*11878SVenu.Iyer@Sun.COM uath_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2894*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t prh)
2895*11878SVenu.Iyer@Sun.COM {
2896*11878SVenu.Iyer@Sun.COM struct uath_softc *sc = (struct uath_softc *)arg;
2897*11878SVenu.Iyer@Sun.COM
2898*11878SVenu.Iyer@Sun.COM ieee80211_propinfo(&sc->sc_ic, pr_name, wldp_pr_num, prh);
2899*11878SVenu.Iyer@Sun.COM }
2900*11878SVenu.Iyer@Sun.COM
290110128SFei.Feng@Sun.COM static int
uath_m_stat(void * arg,uint_t stat,uint64_t * val)290210128SFei.Feng@Sun.COM uath_m_stat(void *arg, uint_t stat, uint64_t *val)
290310128SFei.Feng@Sun.COM {
290410128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)arg;
290510128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
290610128SFei.Feng@Sun.COM struct ieee80211_node *ni = NULL;
290710128SFei.Feng@Sun.COM struct ieee80211_rateset *rs = NULL;
290810128SFei.Feng@Sun.COM
290910128SFei.Feng@Sun.COM UATH_LOCK(sc);
291010128SFei.Feng@Sun.COM switch (stat) {
291110128SFei.Feng@Sun.COM case MAC_STAT_IFSPEED:
291210128SFei.Feng@Sun.COM ni = ic->ic_bss;
291310128SFei.Feng@Sun.COM rs = &ni->in_rates;
291410128SFei.Feng@Sun.COM *val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
291510128SFei.Feng@Sun.COM (rs->ir_rates[ni->in_txrate] & IEEE80211_RATE_VAL)
291610128SFei.Feng@Sun.COM : ic->ic_fixed_rate) * 5000000ull;
291710128SFei.Feng@Sun.COM break;
291810128SFei.Feng@Sun.COM case MAC_STAT_NOXMTBUF:
291910128SFei.Feng@Sun.COM *val = sc->sc_tx_nobuf;
292010128SFei.Feng@Sun.COM break;
292110128SFei.Feng@Sun.COM case MAC_STAT_NORCVBUF:
292210128SFei.Feng@Sun.COM *val = sc->sc_rx_nobuf;
292310128SFei.Feng@Sun.COM break;
292410128SFei.Feng@Sun.COM case MAC_STAT_IERRORS:
292510128SFei.Feng@Sun.COM *val = sc->sc_rx_err;
292610128SFei.Feng@Sun.COM break;
292710128SFei.Feng@Sun.COM case MAC_STAT_RBYTES:
292810128SFei.Feng@Sun.COM *val = ic->ic_stats.is_rx_bytes;
292910128SFei.Feng@Sun.COM break;
293010128SFei.Feng@Sun.COM case MAC_STAT_IPACKETS:
293110128SFei.Feng@Sun.COM *val = ic->ic_stats.is_rx_frags;
293210128SFei.Feng@Sun.COM break;
293310128SFei.Feng@Sun.COM case MAC_STAT_OBYTES:
293410128SFei.Feng@Sun.COM *val = ic->ic_stats.is_tx_bytes;
293510128SFei.Feng@Sun.COM break;
293610128SFei.Feng@Sun.COM case MAC_STAT_OPACKETS:
293710128SFei.Feng@Sun.COM *val = ic->ic_stats.is_tx_frags;
293810128SFei.Feng@Sun.COM break;
293910128SFei.Feng@Sun.COM case MAC_STAT_OERRORS:
294010128SFei.Feng@Sun.COM case WIFI_STAT_TX_FAILED:
294110128SFei.Feng@Sun.COM *val = sc->sc_tx_err;
294210128SFei.Feng@Sun.COM break;
294310128SFei.Feng@Sun.COM case WIFI_STAT_TX_RETRANS:
294410128SFei.Feng@Sun.COM *val = sc->sc_tx_retries;
294510128SFei.Feng@Sun.COM break;
294610128SFei.Feng@Sun.COM case WIFI_STAT_FCS_ERRORS:
294710128SFei.Feng@Sun.COM case WIFI_STAT_WEP_ERRORS:
294810128SFei.Feng@Sun.COM case WIFI_STAT_TX_FRAGS:
294910128SFei.Feng@Sun.COM case WIFI_STAT_MCAST_TX:
295010128SFei.Feng@Sun.COM case WIFI_STAT_RTS_SUCCESS:
295110128SFei.Feng@Sun.COM case WIFI_STAT_RTS_FAILURE:
295210128SFei.Feng@Sun.COM case WIFI_STAT_ACK_FAILURE:
295310128SFei.Feng@Sun.COM case WIFI_STAT_RX_FRAGS:
295410128SFei.Feng@Sun.COM case WIFI_STAT_MCAST_RX:
295510128SFei.Feng@Sun.COM case WIFI_STAT_RX_DUPS:
295610128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
295710128SFei.Feng@Sun.COM return (ieee80211_stat(ic, stat, val));
295810128SFei.Feng@Sun.COM default:
295910128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
296010128SFei.Feng@Sun.COM return (ENOTSUP);
296110128SFei.Feng@Sun.COM }
296210128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
296310128SFei.Feng@Sun.COM
296410128SFei.Feng@Sun.COM return (0);
296510128SFei.Feng@Sun.COM }
296610128SFei.Feng@Sun.COM
296710128SFei.Feng@Sun.COM static mblk_t *
uath_m_tx(void * arg,mblk_t * mp)296810128SFei.Feng@Sun.COM uath_m_tx(void *arg, mblk_t *mp)
296910128SFei.Feng@Sun.COM {
297010128SFei.Feng@Sun.COM struct uath_softc *sc = (struct uath_softc *)arg;
297110128SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
297210128SFei.Feng@Sun.COM mblk_t *next;
297310128SFei.Feng@Sun.COM
297410128SFei.Feng@Sun.COM /*
297510128SFei.Feng@Sun.COM * No data frames go out unless we're associated; this
297610128SFei.Feng@Sun.COM * should not happen as the 802.11 layer does not enable
297710128SFei.Feng@Sun.COM * the xmit queue until we enter the RUN state.
297810128SFei.Feng@Sun.COM */
297910128SFei.Feng@Sun.COM if ((ic->ic_state != IEEE80211_S_RUN) ||
298010128SFei.Feng@Sun.COM UATH_IS_SUSPEND(sc)) {
298110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_m_tx(): "
298210128SFei.Feng@Sun.COM "discard, state %u\n", ic->ic_state);
298310128SFei.Feng@Sun.COM freemsgchain(mp);
298410128SFei.Feng@Sun.COM return (NULL);
298510128SFei.Feng@Sun.COM }
298610128SFei.Feng@Sun.COM
298710128SFei.Feng@Sun.COM while (mp != NULL) {
298810128SFei.Feng@Sun.COM next = mp->b_next;
298910128SFei.Feng@Sun.COM mp->b_next = NULL;
299010128SFei.Feng@Sun.COM if (uath_send(ic, mp, IEEE80211_FC0_TYPE_DATA) != DDI_SUCCESS) {
299110128SFei.Feng@Sun.COM mp->b_next = next;
299210128SFei.Feng@Sun.COM break;
299310128SFei.Feng@Sun.COM }
299410128SFei.Feng@Sun.COM mp = next;
299510128SFei.Feng@Sun.COM }
299610128SFei.Feng@Sun.COM return (mp);
299710128SFei.Feng@Sun.COM }
299810128SFei.Feng@Sun.COM
299910128SFei.Feng@Sun.COM static int
uath_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)300010128SFei.Feng@Sun.COM uath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
300110128SFei.Feng@Sun.COM {
300210128SFei.Feng@Sun.COM struct uath_softc *sc;
300310128SFei.Feng@Sun.COM struct ieee80211com *ic;
300410128SFei.Feng@Sun.COM
300510128SFei.Feng@Sun.COM int i, err, instance;
300610128SFei.Feng@Sun.COM char strbuf[32];
300710128SFei.Feng@Sun.COM uint16_t vendor_id, product_id;
300810128SFei.Feng@Sun.COM
300910128SFei.Feng@Sun.COM wifi_data_t wd = { 0 };
301010128SFei.Feng@Sun.COM mac_register_t *macp;
301110128SFei.Feng@Sun.COM
301210128SFei.Feng@Sun.COM switch (cmd) {
301310128SFei.Feng@Sun.COM case DDI_ATTACH:
301410128SFei.Feng@Sun.COM break;
301510128SFei.Feng@Sun.COM case DDI_RESUME:
301610128SFei.Feng@Sun.COM sc = ddi_get_soft_state(uath_soft_state_p,
301710128SFei.Feng@Sun.COM ddi_get_instance(devinfo));
301810128SFei.Feng@Sun.COM ASSERT(sc != NULL);
301910128SFei.Feng@Sun.COM uath_resume(sc);
302010128SFei.Feng@Sun.COM return (DDI_SUCCESS);
302110128SFei.Feng@Sun.COM default:
302210128SFei.Feng@Sun.COM return (DDI_FAILURE);
302310128SFei.Feng@Sun.COM }
302410128SFei.Feng@Sun.COM
302510128SFei.Feng@Sun.COM instance = ddi_get_instance(devinfo);
302610128SFei.Feng@Sun.COM err = ddi_soft_state_zalloc(uath_soft_state_p, instance);
302710128SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
302810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
302910128SFei.Feng@Sun.COM "ddi_soft_state_zalloc failed\n");
303010128SFei.Feng@Sun.COM return (DDI_FAILURE);
303110128SFei.Feng@Sun.COM }
303210128SFei.Feng@Sun.COM
303310128SFei.Feng@Sun.COM sc = ddi_get_soft_state(uath_soft_state_p, instance);
303410128SFei.Feng@Sun.COM ic = (ieee80211com_t *)&sc->sc_ic;
303510128SFei.Feng@Sun.COM sc->sc_dev = devinfo;
303610128SFei.Feng@Sun.COM
303710128SFei.Feng@Sun.COM err = usb_client_attach(devinfo, USBDRV_VERSION, 0);
303810128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
303910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
304010128SFei.Feng@Sun.COM "usb_client_attach failed\n");
304110128SFei.Feng@Sun.COM goto fail1;
304210128SFei.Feng@Sun.COM }
304310128SFei.Feng@Sun.COM
304410128SFei.Feng@Sun.COM err = usb_get_dev_data(devinfo, &sc->sc_udev, USB_PARSE_LVL_ALL, 0);
304510128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
304610128SFei.Feng@Sun.COM sc->sc_udev = NULL;
304710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
304810128SFei.Feng@Sun.COM "usb_get_dev_data failed\n");
304910128SFei.Feng@Sun.COM goto fail2;
305010128SFei.Feng@Sun.COM }
305110128SFei.Feng@Sun.COM
305210128SFei.Feng@Sun.COM vendor_id = sc->sc_udev->dev_descr->idVendor;
305310128SFei.Feng@Sun.COM product_id = sc->sc_udev->dev_descr->idProduct;
305410128SFei.Feng@Sun.COM sc->dev_flags = uath_lookup(vendor_id, product_id);
305510128SFei.Feng@Sun.COM if (sc->dev_flags == UATH_FLAG_ERR) {
305610128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
305710128SFei.Feng@Sun.COM "HW does not match\n");
305810128SFei.Feng@Sun.COM goto fail2;
305910128SFei.Feng@Sun.COM }
306010128SFei.Feng@Sun.COM
306110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_attach(): "
306210128SFei.Feng@Sun.COM "vendorId = %x,deviceID = %x, flags = %x\n",
306310128SFei.Feng@Sun.COM vendor_id, product_id, sc->dev_flags);
306410128SFei.Feng@Sun.COM
306510128SFei.Feng@Sun.COM /*
306610128SFei.Feng@Sun.COM * We must open the pipes early because they're used to upload the
306710128SFei.Feng@Sun.COM * firmware (pre-firmware devices) or to send firmware commands.
306810128SFei.Feng@Sun.COM */
306910128SFei.Feng@Sun.COM err = uath_open_pipes(sc);
307010128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
307110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
307210128SFei.Feng@Sun.COM "could not open pipes\n");
307310128SFei.Feng@Sun.COM goto fail3;
307410128SFei.Feng@Sun.COM }
307510128SFei.Feng@Sun.COM
307610128SFei.Feng@Sun.COM if (sc->dev_flags & UATH_FLAG_PRE_FIRMWARE) {
307710128SFei.Feng@Sun.COM err = uath_loadfirmware(sc);
307810128SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
307910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
308010128SFei.Feng@Sun.COM "could not read firmware %s, err %d\n",
308110128SFei.Feng@Sun.COM "uath-ar5523", err);
308210128SFei.Feng@Sun.COM goto fail3;
308310128SFei.Feng@Sun.COM }
308410128SFei.Feng@Sun.COM
308510128SFei.Feng@Sun.COM uath_close_pipes(sc);
308610128SFei.Feng@Sun.COM usb_client_detach(sc->sc_dev, sc->sc_udev);
308710128SFei.Feng@Sun.COM
308810128SFei.Feng@Sun.COM err = usb_reset_device(devinfo, USB_RESET_LVL_REATTACH);
308910128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
309010128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
309110128SFei.Feng@Sun.COM "could not re-attach, err %d\n", err);
309210128SFei.Feng@Sun.COM goto fail1;
309310128SFei.Feng@Sun.COM }
309410128SFei.Feng@Sun.COM return (DDI_SUCCESS);
309510128SFei.Feng@Sun.COM }
309610128SFei.Feng@Sun.COM
309710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_attach(): "
309810128SFei.Feng@Sun.COM "firmware download and re-attach successfully\n");
309910128SFei.Feng@Sun.COM
310010128SFei.Feng@Sun.COM /*
310110128SFei.Feng@Sun.COM * Only post-firmware devices here.
310210128SFei.Feng@Sun.COM */
310310128SFei.Feng@Sun.COM mutex_init(&sc->sc_genlock, NULL, MUTEX_DRIVER, NULL);
310410128SFei.Feng@Sun.COM mutex_init(&sc->sc_rxlock_cmd, NULL, MUTEX_DRIVER, NULL);
310510128SFei.Feng@Sun.COM mutex_init(&sc->sc_txlock_cmd, NULL, MUTEX_DRIVER, NULL);
310610128SFei.Feng@Sun.COM mutex_init(&sc->sc_rxlock_data, NULL, MUTEX_DRIVER, NULL);
310710128SFei.Feng@Sun.COM mutex_init(&sc->sc_txlock_data, NULL, MUTEX_DRIVER, NULL);
310810128SFei.Feng@Sun.COM
310910128SFei.Feng@Sun.COM /*
311010128SFei.Feng@Sun.COM * Allocate xfers for firmware commands.
311110128SFei.Feng@Sun.COM */
311210128SFei.Feng@Sun.COM err = uath_alloc_cmd_list(sc, sc->sc_cmd, UATH_CMD_LIST_COUNT,
311310128SFei.Feng@Sun.COM UATH_MAX_CMDSZ);
311410128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
311510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
311610128SFei.Feng@Sun.COM "could not allocate Tx command list\n");
311710128SFei.Feng@Sun.COM goto fail4;
311810128SFei.Feng@Sun.COM }
311910128SFei.Feng@Sun.COM
312010128SFei.Feng@Sun.COM err = uath_init_cmd_list(sc);
312110128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
312210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
312310128SFei.Feng@Sun.COM "could not init RX command list\n");
312410128SFei.Feng@Sun.COM goto fail5;
312510128SFei.Feng@Sun.COM }
312610128SFei.Feng@Sun.COM
312710128SFei.Feng@Sun.COM /*
312810128SFei.Feng@Sun.COM * We're now ready to send+receive firmware commands.
312910128SFei.Feng@Sun.COM */
313010128SFei.Feng@Sun.COM err = uath_host_available(sc);
313110128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
313210128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
313310128SFei.Feng@Sun.COM "could not initialize adapter\n");
313410128SFei.Feng@Sun.COM goto fail5;
313510128SFei.Feng@Sun.COM }
313610128SFei.Feng@Sun.COM
313710128SFei.Feng@Sun.COM err = uath_get_devcap(sc);
313810128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
313910128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
314010128SFei.Feng@Sun.COM "could not get device capabilities\n");
314110128SFei.Feng@Sun.COM goto fail5;
314210128SFei.Feng@Sun.COM }
314310128SFei.Feng@Sun.COM
314410128SFei.Feng@Sun.COM err = uath_get_devstatus(sc, ic->ic_macaddr);
314510128SFei.Feng@Sun.COM if (err != UATH_SUCCESS) {
314610128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
314710128SFei.Feng@Sun.COM "could not get dev status\n");
314810128SFei.Feng@Sun.COM goto fail5;
314910128SFei.Feng@Sun.COM }
315010128SFei.Feng@Sun.COM
315110128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_attach(): "
315210128SFei.Feng@Sun.COM "MAC address is: %x:%x:%x:%x:%x:%x\n",
315310128SFei.Feng@Sun.COM ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2],
315410128SFei.Feng@Sun.COM ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5]);
315510128SFei.Feng@Sun.COM
315610128SFei.Feng@Sun.COM ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
315710128SFei.Feng@Sun.COM ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
315810128SFei.Feng@Sun.COM ic->ic_state = IEEE80211_S_INIT;
315910128SFei.Feng@Sun.COM
316010128SFei.Feng@Sun.COM ic->ic_maxrssi = 40;
316110128SFei.Feng@Sun.COM
316210128SFei.Feng@Sun.COM ic->ic_xmit = uath_send;
316310128SFei.Feng@Sun.COM
316410128SFei.Feng@Sun.COM /* set device capabilities */
316510128SFei.Feng@Sun.COM ic->ic_caps =
316610128SFei.Feng@Sun.COM IEEE80211_C_TXPMGT | /* tx power management */
316710128SFei.Feng@Sun.COM IEEE80211_C_SHPREAMBLE | /* short preamble supported */
316810128SFei.Feng@Sun.COM IEEE80211_C_SHSLOT; /* short slot time supported */
316910128SFei.Feng@Sun.COM
317010128SFei.Feng@Sun.COM ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */
317110128SFei.Feng@Sun.COM
317210128SFei.Feng@Sun.COM /* set supported .11b and .11g rates */
317310128SFei.Feng@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11B] = uath_rateset_11b;
317410128SFei.Feng@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11G] = uath_rateset_11g;
317510128SFei.Feng@Sun.COM
317610128SFei.Feng@Sun.COM /* set supported .11b and .11g channels (1 through 11) */
317710128SFei.Feng@Sun.COM for (i = 1; i <= 11; i++) {
317810128SFei.Feng@Sun.COM ic->ic_sup_channels[i].ich_freq =
317910128SFei.Feng@Sun.COM ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
318010128SFei.Feng@Sun.COM ic->ic_sup_channels[i].ich_flags =
318110128SFei.Feng@Sun.COM IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
318210128SFei.Feng@Sun.COM IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
318310128SFei.Feng@Sun.COM }
318410128SFei.Feng@Sun.COM
318510128SFei.Feng@Sun.COM ieee80211_attach(ic);
318610128SFei.Feng@Sun.COM
318710128SFei.Feng@Sun.COM /* register WPA door */
318810128SFei.Feng@Sun.COM ieee80211_register_door(ic, ddi_driver_name(devinfo),
318910128SFei.Feng@Sun.COM ddi_get_instance(devinfo));
319010128SFei.Feng@Sun.COM
319110128SFei.Feng@Sun.COM /* override state transition machine */
319210128SFei.Feng@Sun.COM sc->sc_newstate = ic->ic_newstate;
319310128SFei.Feng@Sun.COM ic->ic_newstate = uath_newstate;
319410128SFei.Feng@Sun.COM ieee80211_media_init(ic);
319510128SFei.Feng@Sun.COM ic->ic_def_txkey = 0;
319610128SFei.Feng@Sun.COM
319710128SFei.Feng@Sun.COM sc->sc_flags = 0;
319810128SFei.Feng@Sun.COM
319910128SFei.Feng@Sun.COM /*
320010128SFei.Feng@Sun.COM * Provide initial settings for the WiFi plugin; whenever this
320110128SFei.Feng@Sun.COM * information changes, we need to call mac_plugindata_update()
320210128SFei.Feng@Sun.COM */
320310128SFei.Feng@Sun.COM wd.wd_opmode = ic->ic_opmode;
320410128SFei.Feng@Sun.COM wd.wd_secalloc = WIFI_SEC_NONE;
320510128SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
320610128SFei.Feng@Sun.COM
320710128SFei.Feng@Sun.COM if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
320810128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath_attach(): "
320910128SFei.Feng@Sun.COM "MAC version mismatch\n");
321010128SFei.Feng@Sun.COM goto fail5;
321110128SFei.Feng@Sun.COM }
321210128SFei.Feng@Sun.COM
321310128SFei.Feng@Sun.COM macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI;
321410128SFei.Feng@Sun.COM macp->m_driver = sc;
321510128SFei.Feng@Sun.COM macp->m_dip = devinfo;
321610128SFei.Feng@Sun.COM macp->m_src_addr = ic->ic_macaddr;
321710128SFei.Feng@Sun.COM macp->m_callbacks = &uath_m_callbacks;
321810128SFei.Feng@Sun.COM macp->m_min_sdu = 0;
321910128SFei.Feng@Sun.COM macp->m_max_sdu = IEEE80211_MTU;
322010128SFei.Feng@Sun.COM macp->m_pdata = &wd;
322110128SFei.Feng@Sun.COM macp->m_pdata_size = sizeof (wd);
322210128SFei.Feng@Sun.COM
322310128SFei.Feng@Sun.COM err = mac_register(macp, &ic->ic_mach);
322410128SFei.Feng@Sun.COM mac_free(macp);
322510128SFei.Feng@Sun.COM if (err != 0) {
322610128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath_attach(): "
322710128SFei.Feng@Sun.COM "mac_register() error %x\n", err);
322810128SFei.Feng@Sun.COM goto fail5;
322910128SFei.Feng@Sun.COM };
323010128SFei.Feng@Sun.COM
323110128SFei.Feng@Sun.COM err = usb_register_hotplug_cbs(devinfo,
323210128SFei.Feng@Sun.COM uath_disconnect, uath_reconnect);
323310128SFei.Feng@Sun.COM if (err != USB_SUCCESS) {
323410128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
323510128SFei.Feng@Sun.COM "failed to register events\n");
323610128SFei.Feng@Sun.COM goto fail6;
323710128SFei.Feng@Sun.COM }
323810128SFei.Feng@Sun.COM
323910128SFei.Feng@Sun.COM /*
324010128SFei.Feng@Sun.COM * Create minor node of type DDI_NT_NET_WIFI
324110128SFei.Feng@Sun.COM */
324210128SFei.Feng@Sun.COM (void) snprintf(strbuf, sizeof (strbuf), "%s%d",
324310128SFei.Feng@Sun.COM "uath", instance);
324410128SFei.Feng@Sun.COM err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR,
324510128SFei.Feng@Sun.COM instance + 1, DDI_NT_NET_WIFI, 0);
324610128SFei.Feng@Sun.COM if (err != DDI_SUCCESS)
324710128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_ERR, "uath: uath_attach(): "
324810128SFei.Feng@Sun.COM "ddi_create_minor_node() failed\n");
324910128SFei.Feng@Sun.COM
325010128SFei.Feng@Sun.COM /*
325110128SFei.Feng@Sun.COM * Notify link is down now
325210128SFei.Feng@Sun.COM */
325310128SFei.Feng@Sun.COM mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
325410128SFei.Feng@Sun.COM
325510128SFei.Feng@Sun.COM UATH_DEBUG(UATH_DBG_MSG, "uath: uath_attach(): "
325610128SFei.Feng@Sun.COM "attach success\n");
325710128SFei.Feng@Sun.COM return (DDI_SUCCESS);
325810128SFei.Feng@Sun.COM
325910128SFei.Feng@Sun.COM fail6:
326010128SFei.Feng@Sun.COM (void) mac_unregister(ic->ic_mach);
326110128SFei.Feng@Sun.COM fail5:
326210128SFei.Feng@Sun.COM uath_free_cmd_list(sc->sc_cmd, UATH_CMD_LIST_COUNT);
326310128SFei.Feng@Sun.COM fail4:
326410128SFei.Feng@Sun.COM mutex_destroy(&sc->sc_genlock);
326510128SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock_cmd);
326610128SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock_data);
326710128SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock_cmd);
326810128SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock_data);
326910128SFei.Feng@Sun.COM fail3:
327010128SFei.Feng@Sun.COM uath_close_pipes(sc);
327110128SFei.Feng@Sun.COM fail2:
327210128SFei.Feng@Sun.COM usb_client_detach(sc->sc_dev, sc->sc_udev);
327310128SFei.Feng@Sun.COM fail1:
327410128SFei.Feng@Sun.COM ddi_soft_state_free(uath_soft_state_p, ddi_get_instance(devinfo));
327510128SFei.Feng@Sun.COM return (DDI_FAILURE);
327610128SFei.Feng@Sun.COM }
327710128SFei.Feng@Sun.COM
327810128SFei.Feng@Sun.COM static int
uath_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)327910128SFei.Feng@Sun.COM uath_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
328010128SFei.Feng@Sun.COM {
328110128SFei.Feng@Sun.COM struct uath_softc *sc;
328210128SFei.Feng@Sun.COM
328310128SFei.Feng@Sun.COM sc = ddi_get_soft_state(uath_soft_state_p, ddi_get_instance(devinfo));
328410128SFei.Feng@Sun.COM ASSERT(sc != NULL);
328510128SFei.Feng@Sun.COM
328610128SFei.Feng@Sun.COM switch (cmd) {
328710128SFei.Feng@Sun.COM case DDI_DETACH:
328810128SFei.Feng@Sun.COM break;
328910128SFei.Feng@Sun.COM case DDI_SUSPEND:
329010128SFei.Feng@Sun.COM if (UATH_IS_RUNNING(sc)) {
329110128SFei.Feng@Sun.COM ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
329210128SFei.Feng@Sun.COM uath_stop(sc);
329310128SFei.Feng@Sun.COM }
329410128SFei.Feng@Sun.COM UATH_LOCK(sc);
329510128SFei.Feng@Sun.COM sc->sc_flags &= ~UATH_FLAG_RUNNING;
329610128SFei.Feng@Sun.COM sc->sc_flags |= UATH_FLAG_SUSPEND;
329710128SFei.Feng@Sun.COM UATH_UNLOCK(sc);
329810128SFei.Feng@Sun.COM return (DDI_SUCCESS);
329910128SFei.Feng@Sun.COM default:
330010128SFei.Feng@Sun.COM return (DDI_FAILURE);
330110128SFei.Feng@Sun.COM }
330210128SFei.Feng@Sun.COM
330310128SFei.Feng@Sun.COM if (sc->dev_flags & UATH_FLAG_PRE_FIRMWARE) {
330410128SFei.Feng@Sun.COM ddi_soft_state_free(uath_soft_state_p,
330510128SFei.Feng@Sun.COM ddi_get_instance(devinfo));
330610128SFei.Feng@Sun.COM return (DDI_SUCCESS);
330710128SFei.Feng@Sun.COM }
330810128SFei.Feng@Sun.COM
330910128SFei.Feng@Sun.COM if (!UATH_IS_DISCONNECT(sc) && UATH_IS_RUNNING(sc))
331010128SFei.Feng@Sun.COM uath_stop(sc);
331110128SFei.Feng@Sun.COM
331210128SFei.Feng@Sun.COM uath_free_cmd_list(sc->sc_cmd, UATH_CMD_LIST_COUNT);
331310128SFei.Feng@Sun.COM
331410128SFei.Feng@Sun.COM if (mac_disable(sc->sc_ic.ic_mach) != 0)
331510128SFei.Feng@Sun.COM return (DDI_FAILURE);
331610128SFei.Feng@Sun.COM
331710128SFei.Feng@Sun.COM /*
331810128SFei.Feng@Sun.COM * Unregister from the MAC layer subsystem
331910128SFei.Feng@Sun.COM */
332010128SFei.Feng@Sun.COM if (mac_unregister(sc->sc_ic.ic_mach) != 0)
332110128SFei.Feng@Sun.COM return (DDI_FAILURE);
332210128SFei.Feng@Sun.COM
332310128SFei.Feng@Sun.COM /*
332410128SFei.Feng@Sun.COM * detach ieee80211 layer
332510128SFei.Feng@Sun.COM */
332610128SFei.Feng@Sun.COM ieee80211_detach(&sc->sc_ic);
332710128SFei.Feng@Sun.COM
332810128SFei.Feng@Sun.COM /* close Tx/Rx pipes */
332910128SFei.Feng@Sun.COM uath_close_pipes(sc);
333010128SFei.Feng@Sun.COM usb_unregister_hotplug_cbs(devinfo);
333110128SFei.Feng@Sun.COM
333210128SFei.Feng@Sun.COM mutex_destroy(&sc->sc_genlock);
333310128SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock_cmd);
333410128SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock_data);
333510128SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock_cmd);
333610128SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock_data);
333710128SFei.Feng@Sun.COM
333810128SFei.Feng@Sun.COM /* pipes will be close in uath_stop() */
333910128SFei.Feng@Sun.COM usb_client_detach(devinfo, sc->sc_udev);
334010128SFei.Feng@Sun.COM sc->sc_udev = NULL;
334110128SFei.Feng@Sun.COM
334210128SFei.Feng@Sun.COM ddi_remove_minor_node(devinfo, NULL);
334310128SFei.Feng@Sun.COM ddi_soft_state_free(uath_soft_state_p, ddi_get_instance(devinfo));
334410128SFei.Feng@Sun.COM
334510128SFei.Feng@Sun.COM return (DDI_SUCCESS);
334610128SFei.Feng@Sun.COM }
334710128SFei.Feng@Sun.COM
334810128SFei.Feng@Sun.COM int
_info(struct modinfo * modinfop)334910128SFei.Feng@Sun.COM _info(struct modinfo *modinfop)
335010128SFei.Feng@Sun.COM {
335110128SFei.Feng@Sun.COM return (mod_info(&modlinkage, modinfop));
335210128SFei.Feng@Sun.COM }
335310128SFei.Feng@Sun.COM
335410128SFei.Feng@Sun.COM int
_init(void)335510128SFei.Feng@Sun.COM _init(void)
335610128SFei.Feng@Sun.COM {
335710128SFei.Feng@Sun.COM int status;
335810128SFei.Feng@Sun.COM
335910128SFei.Feng@Sun.COM status = ddi_soft_state_init(&uath_soft_state_p,
336010128SFei.Feng@Sun.COM sizeof (struct uath_softc), 1);
336110128SFei.Feng@Sun.COM if (status != 0)
336210128SFei.Feng@Sun.COM return (status);
336310128SFei.Feng@Sun.COM
336410128SFei.Feng@Sun.COM mac_init_ops(&uath_dev_ops, "uath");
336510128SFei.Feng@Sun.COM status = mod_install(&modlinkage);
336610128SFei.Feng@Sun.COM if (status != 0) {
336710128SFei.Feng@Sun.COM mac_fini_ops(&uath_dev_ops);
336810128SFei.Feng@Sun.COM ddi_soft_state_fini(&uath_soft_state_p);
336910128SFei.Feng@Sun.COM }
337010128SFei.Feng@Sun.COM return (status);
337110128SFei.Feng@Sun.COM }
337210128SFei.Feng@Sun.COM
337310128SFei.Feng@Sun.COM int
_fini(void)337410128SFei.Feng@Sun.COM _fini(void)
337510128SFei.Feng@Sun.COM {
337610128SFei.Feng@Sun.COM int status;
337710128SFei.Feng@Sun.COM
337810128SFei.Feng@Sun.COM status = mod_remove(&modlinkage);
337910128SFei.Feng@Sun.COM if (status == 0) {
338010128SFei.Feng@Sun.COM mac_fini_ops(&uath_dev_ops);
338110128SFei.Feng@Sun.COM ddi_soft_state_fini(&uath_soft_state_p);
338210128SFei.Feng@Sun.COM }
338310128SFei.Feng@Sun.COM return (status);
338410128SFei.Feng@Sun.COM }
3385