uath.c revision 0dc2366f7b9f9f36e10909b1e95edbf2a261c2ac
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2006 Sam Leffler, Errno Consulting
* Copyright (c) 2008-2009 Weongyo Jeong <weongyo@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*/
/*
* This driver is distantly derived from a driver of the same name
* by Damien Bergamini. The original copyright is included below:
*
* Copyright (c) 2006
* Damien Bergamini <damien.bergamini@free.fr>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/byteorder.h>
#include <sys/mac_provider.h>
#include <sys/mac_wifi.h>
#include <sys/net80211.h>
#define USBDRV_MAJOR_VER 2
#define USBDRV_MINOR_VER 0
#include "uath_reg.h"
#include "uath_var.h"
static void *uath_soft_state_p = NULL;
/*
* Bit flags in the ral_dbg_flags
*/
#define UATH_DBG_MSG 0x000001
#define UATH_DBG_ERR 0x000002
#define UATH_DBG_USB 0x000004
#define UATH_DBG_TX 0x000008
#define UATH_DBG_RX 0x000010
#define UATH_DBG_FW 0x000020
#define UATH_DBG_TX_CMD 0x000040
#define UATH_DBG_RX_CMD 0x000080
#define UATH_DBG_ALL 0x000fff
uint32_t uath_dbg_flags = 0;
#ifdef DEBUG
#define UATH_DEBUG \
#else
#define UATH_DEBUG
#endif
/*
* UB51: AR5005UG 802.11b/g, UB52: AR5005UX 802.11b/g
*/
#define UATH_FLAG_PRE_FIRMWARE (1 << 0)
#define UATH_DEV(v, p, f) \
{ { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }, (f) }, \
(f) | UATH_FLAG_PRE_FIRMWARE }
#define UATH_DEV_UG(v, p) UATH_DEV(v, p, 0)
struct uath_devno {
};
static const struct uath_type {
struct uath_devno dev;
} uath_devs[] = {
};
static char uath_fwmod[] = "uathfw";
static char uath_binmod[] = "uathbin";
/*
* Supported rates for 802.11b/g modes (in 500Kbps unit).
*/
static const struct ieee80211_rateset uath_rateset_11b =
{ 4, { 2, 4, 11, 22 } };
static const struct ieee80211_rateset uath_rateset_11g =
{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
/*
* device operations
*/
/*
* Module Loading Data & Entry Points
*/
static struct modldrv uath_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
"Atheros AR5523 USB Driver v1.1", /* short description */
&uath_dev_ops /* driver specific ops */
};
static struct modlinkage modlinkage = {
(void *)&uath_modldrv,
};
static int uath_m_start(void *);
static void uath_m_stop(void *);
static int uath_m_promisc(void *, boolean_t);
static int uath_m_unicst(void *, const uint8_t *);
static int uath_m_setprop(void *, const char *, mac_prop_id_t,
uint_t, const void *);
static int uath_m_getprop(void *, const char *, mac_prop_id_t,
uint_t, void *);
static void uath_m_propinfo(void *, const char *, mac_prop_id_t,
static mac_callbacks_t uath_m_callbacks = {
NULL,
NULL,
NULL,
NULL,
};
static usb_alt_if_data_t *
static usb_ep_data_t *
static const char *
uath_codename(int code);
static void uath_list_all_eps(usb_alt_if_data_t *);
static int uath_open_pipes(struct uath_softc *);
static void uath_close_pipes(struct uath_softc *);
static int uath_fw_send(struct uath_softc *,
usb_pipe_handle_t, const void *, size_t);
static int uath_fw_ack(struct uath_softc *, int);
static int uath_loadfirmware(struct uath_softc *);
static int uath_alloc_cmd_list(struct uath_softc *,
struct uath_cmd *, int, int);
static int uath_init_cmd_list(struct uath_softc *);
static void uath_free_cmd_list(struct uath_cmd *, int);
static int uath_host_available(struct uath_softc *);
static int uath_get_devcap(struct uath_softc *);
static void uath_cmd_lock_init(struct uath_cmd_lock *);
static void uath_cmd_lock_destroy(struct uath_cmd_lock *);
static void uath_cmd_lock_signal(struct uath_cmd_lock *);
int, void *, int, int);
int, int);
const void *, int, void *, int, int);
static int uath_rx_cmd_xfer(struct uath_softc *);
static int uath_tx_cmd_xfer(struct uath_softc *,
usb_pipe_handle_t, const void *, uint_t);
static void uath_init_data_queue(struct uath_softc *);
static int uath_set_rates(struct uath_softc *,
const struct ieee80211_rateset *);
static int uath_write_associd(struct uath_softc *);
static int uath_set_ledsteady(struct uath_softc *, int, int);
static int uath_set_ledblink(struct uath_softc *, int, int, int, int);
static int uath_reconnect(dev_info_t *);
static int uath_disconnect(dev_info_t *);
static int uath_dataflush(struct uath_softc *);
static int uath_cmdflush(struct uath_softc *);
static int uath_flush(struct uath_softc *);
static int uath_set_ledstate(struct uath_softc *, int);
static int uath_reset_tx_queues(struct uath_softc *);
static int uath_wme_init(struct uath_softc *);
static int uath_config_multi(struct uath_softc *,
uint32_t, const void *, int);
static int uath_switch_channel(struct uath_softc *,
struct ieee80211_channel *);
static int uath_init_locked(void *);
static void uath_stop_locked(void *);
static int uath_init(struct uath_softc *);
static void uath_stop(struct uath_softc *);
static void uath_resume(struct uath_softc *);
static void
{
if (dbg_flags & uath_dbg_flags) {
}
}
static uint_t
{
int i, size;
for (i = 0; i < size; i++) {
}
return (UATH_FLAG_ERR);
}
/*
* Return a specific alt_if from the device descriptor tree.
*/
static usb_alt_if_data_t *
{
/*
* Assume everything is in the tree for now,
* (USB_PARSE_LVL_ALL)
* so we can directly index the array.
*/
/* Descend to configuration, configs are 1-based */
return (NULL);
/* Descend to interface */
return (NULL);
/* Descend to alt */
return (NULL);
return (if_alt_data);
}
/*
* Print all endpoints of an alt_if.
*/
static void
{
int i;
for (i = 0; i < ifalt->altif_n_ep; i++) {
"uath: uath_list_all_endpoint: "
"ep addresa[%x] is %x",
i, ep_descr->bEndpointAddress);
}
}
static usb_ep_data_t *
{
int i;
return (NULL);
for (i = 0; i < altif_data->altif_n_ep; i++) {
continue;
return (&altif_data->altif_ep[i]);
}
return (NULL);
}
/*
* Open communication pipes.
* The following pipes are used by the AR5523:
* ep0: 0x81 IN Rx cmd
* ep1: 0x01 OUT Tx cmd
* ep2: 0x82 IN Rx data
* ep3: 0x02 OUT Tx data
*/
static int
{
int err;
#ifdef DEBUG
if (altif_data == NULL) {
return (USB_FAILURE);
}
#endif
/*
* XXX pipes numbers are hardcoded because we don't have any way
* to distinguish the data pipes from the firmware command pipes
* (both are bulk pipes) using the endpoints descriptors.
*/
0, 0, 0x81, USB_EP_ATTR_BULK);
if (err != USB_SUCCESS) {
"failed to open rx data pipe, err = %x\n",
err);
goto fail;
}
0, 0, 0x01, USB_EP_ATTR_BULK);
"find pipe %x\n",
if (err != USB_SUCCESS) {
"failed to open tx command pipe, err = %x\n",
err);
goto fail;
}
0, 0, 0x82, USB_EP_ATTR_BULK);
"find pipe %x\n",
if (err != USB_SUCCESS) {
"failed to open tx pipe, err = %x\n",
err);
goto fail;
}
0, 0, 0x02, USB_EP_ATTR_BULK);
"find pipe %x\n",
if (err != USB_SUCCESS) {
"failed to open rx command pipe, err = %x\n",
err);
goto fail;
}
return (UATH_SUCCESS);
fail:
return (err);
}
static void
{
}
}
}
}
}
static const char *
uath_codename(int code)
{
#define N(a) (sizeof (a)/sizeof (a[0]))
static const char *names[] = {
"0x00",
"HOST_AVAILABLE",
"BIND",
"TARGET_RESET",
"TARGET_GET_CAPABILITY",
"TARGET_SET_CONFIG",
"TARGET_GET_STATUS",
"TARGET_GET_STATS",
"TARGET_START",
"TARGET_STOP",
"TARGET_ENABLE",
"TARGET_DISABLE",
"CREATE_CONNECTION",
"UPDATE_CONNECT_ATTR",
"DELETE_CONNECT",
"SEND",
"FLUSH",
"STATS_UPDATE",
"BMISS",
"DEVICE_AVAIL",
"SEND_COMPLETE",
"DATA_AVAIL",
"SET_PWR_MODE",
"BMISS_ACK",
"SET_LED_STEADY",
"SET_LED_BLINK",
"SETUP_BEACON_DESC",
"BEACON_INIT",
"RESET_KEY_CACHE",
"RESET_KEY_CACHE_ENTRY",
"SET_KEY_CACHE_ENTRY",
"SET_DECOMP_MASK",
"SET_REGULATORY_DOMAIN",
"SET_LED_STATE",
"WRITE_ASSOCID",
"SET_STA_BEACON_TIMERS",
"GET_TSF",
"RESET_TSF",
"SET_ADHOC_MODE",
"SET_BASIC_RATE",
"MIB_CONTROL",
"GET_CHANNEL_DATA",
"GET_CUR_RSSI",
"SET_ANTENNA_SWITCH",
"0x2c", "0x2d", "0x2e",
"USE_SHORT_SLOT_TIME",
"SET_POWER_MODE",
"SETUP_PSPOLL_DESC",
"SET_RX_MULTICAST_FILTER",
"RX_FILTER",
"PER_CALIBRATION",
"RESET",
"DISABLE",
"PHY_DISABLE",
"SET_TX_POWER_LIMIT",
"SET_TX_QUEUE_PARAMS",
"SETUP_TX_QUEUE",
"RELEASE_TX_QUEUE",
};
static char buf[8];
if (code == WDCMSG_SET_DEFAULT_KEY)
return ("SET_DEFAULT_KEY");
return (buf);
#undef N
}
static int
{
int res;
if (res != USB_SUCCESS) {
return (UATH_FAILURE);
}
return (UATH_SUCCESS);
}
static int
{
struct uath_fwblock *rxblock;
int err;
"uath: uath_fw_ack(): "
"uath_rx_transfer(): failed to allocate req");
return (UATH_FAILURE);
}
req->bulk_timeout = 0;
if (err != USB_SUCCESS) {
"failed to do rx xfer, %d", err);
return (UATH_FAILURE);
}
"rxblock flags=0x%x total=%d\n",
return (UATH_SUCCESS);
}
/*
* find uath firmware module's "_start" "_end" symbols
* and get its size.
*/
static int
{
char start_sym[64];
char end_sym[64];
char *p, *end;
int rv;
size_t n;
return (UATH_FAILURE);
}
return (UATH_FAILURE);
}
*start = p;
*len = n;
return (UATH_SUCCESS);
}
/*
* Load the MIPS R4000 microcode into the device. Once the image is loaded,
* the device will detach itself from the bus and reattach later with a new
* product Id (a la ezusb). XXX this could also be implemented in userland
*/
static int
{
struct uath_fwblock txblock;
"module %s not found\n", uath_fwmod);
goto label;
}
if (err != UATH_SUCCESS) {
"could not get firmware\n");
goto label;
}
"failed to alloc firmware memory\n");
err = UATH_FAILURE;
goto label;
}
/* bzero(txblock, sizeof (struct uath_fwblock)); */
while (len > 0) {
"sending firmware block: %d bytes sending\n", mlen);
"sending firmware block: %d bytes remaining\n",
/* send firmware block meta-data */
sizeof (struct uath_fwblock));
if (err != UATH_SUCCESS) {
"send block meta-data error");
goto label;
}
/* send firmware block data */
if (err != UATH_SUCCESS) {
"send block data err");
goto label;
}
/* wait for ack from firmware */
if (err != UATH_SUCCESS) {
"rx block ack err");
goto label;
}
}
(void) ddi_modclose(modp);
return (err);
}
static int
{
int i, err;
for (i = 0; i < ncmd; i++) {
"could not allocate xfer buffer\n");
err = DDI_ENOMEM;
goto fail;
}
}
return (UATH_SUCCESS);
fail:
return (err);
}
static int
{
int i;
for (i = 0; i < UATH_CMD_LIST_COUNT; i++) {
"failed to init cmd list %x\n", i);
return (UATH_FAILURE);
}
}
return (UATH_SUCCESS);
}
static void
{
int i;
for (i = 0; i < ncmd; i++)
}
}
static int
{
struct uath_cmd_host_available setup;
/* inform target the host is available */
}
static void
{
int err;
if (err == UATH_SUCCESS)
else
}
static int
{
/* collect device capabilities */
&cap->targetVersion);
&cap->targetRevision);
&cap->macVersion);
&cap->macRevision);
&cap->phyRevision);
&cap->regCapBits);
/* NB: not supported in rev 1.5 */
/* uath_get_capability(sc, CAP_COUNTRY_CODE, cap->countryCode); */
&cap->wirelessModes);
&cap->chanSpreadSupport);
&cap->compressSupport);
&cap->burstSupport);
&cap->fastFramesSupport);
&cap->chapTuningSupport);
&cap->turboGSupport);
&cap->turboPrimeSupport);
&cap->deviceType);
&cap->wmeSupport);
&cap->numTxQueues);
&cap->connectionIdMax);
&cap->low5GhzChan);
&cap->high5GhzChan);
&cap->low2GhzChan);
&cap->high2GhzChan);
&cap->supportCipherTKIP);
&cap->supportMicTKIP);
return (UATH_SUCCESS);
}
static int
{
int err;
if (err != UATH_SUCCESS)
return (err);
}
static int
{
int err;
/* retrieve MAC address */
if (err != UATH_SUCCESS) {
"could not read MAC address\n");
return (err);
}
if (err != UATH_SUCCESS) {
"could not read device serial number\n");
return (err);
}
return (UATH_SUCCESS);
}
/*
* uath_cmd_lock: a special signal structure that is used for notification
* that a callback function has been called.
*/
/* Initializes the uath_cmd_lock structure. */
static void
{
}
/* Deinitalizes the uath_cb_lock structure. */
void
{
}
/*
* Wait on lock until someone calls the "signal" function or the timeout
* expires. Note: timeout is in microseconds.
*/
static int
{
if (timeout < 0) {
/* no timeout - wait as long as needed */
} else {
/* wait with timeout (given in usec) */
if (cv_res <= 0) break;
}
}
return (res);
}
/* Signal that the job (eg. callback) is done and unblock anyone who waits. */
static void
{
}
static int
{
}
static int
{
flags &= ~UATH_CMD_FLAG_READ;
}
/*
* Low-level function to send read or write commands to the firmware.
*/
static int
{
struct uath_cmd_hdr *hdr;
int err;
/* grab a xfer */
/* always bulk-out a multiple of 4 bytes */
"queue %x send %s [flags 0x%x] olen %d\n",
"warning - odata is NULL\n");
"warning - olen %x is short\n, olen");
if (err != UATH_SUCCESS) {
"Error writing command\n");
return (UATH_FAILURE);
}
/* wait at most two seconds for command reply */
if (err != UATH_SUCCESS) {
"timeout waiting for reply, "
"to cmd 0x%x (%u), queue %x\n",
err = UATH_FAILURE;
"unexpected reply data count "
"to cmd 0x%x (%x), got %u, expected %u\n",
err = UATH_FAILURE;
}
return (err);
}
return (UATH_SUCCESS);
}
/* ARGSUSED */
static void
{
"cr:%s(%d), flags:0x%x, tx queued %d\n",
sc->tx_cmd_queued);
sc->tx_cmd_queued--;
}
static int
{
int res;
send_req->bulk_cb_flags = 0;
if (res != UATH_SUCCESS) {
return (UATH_FAILURE);
}
sc->tx_cmd_queued++;
return (UATH_SUCCESS);
}
static void
{
struct uath_cmd_hdr *hdr;
int dlen;
/* NB: msgid is passed thru w/o byte swapping */
"%s: [ix %x] len=%x status %x\n",
/* reply to a read command */
default:
"code %x data len %u\n",
/*
* The first response from the target after the
* HOST_AVAILABLE has an invalid msgid so we must
* treat it specially.
*/
"uath: uath_cmdeof(): "
"invalid WDC msg length %u; "
"msg ignored\n",
return;
}
/*
* first word, if present, always gives the
* number of bytes--unless it's 0 in which
* case a single 32-bit word should be present.
*/
if (olen == 0) {
/* convention is 0 =>'s one word */
/* XXX KASSERT(olen == dlen ) */
}
} else
olen = 0;
/* NB: cmd->olen validated in uath_cmd */
/* XXX complain? */
"uath: uath_cmdeof(): "
"cmd 0x%x olen %u cmd olen %u\n",
}
/* XXX complain, shouldn't happen */
"uath: uath_cmdeof(): "
"cmd 0x%x olen %u dlen %u\n",
}
/* XXX have submitter do this */
/* copy answer into caller's supplied buffer */
}
}
/* Just signal that something happened */
break;
case WDCMSG_TARGET_START:
"receive TARGET STAERT\n");
/* XXX */
return;
}
/* XXX something wrong */
return;
}
/* XXX have submitter do this */
/* copy answer into caller's supplied buffer */
/* wake up caller */
break;
case WDCMSG_SEND_COMPLETE:
/* this notification is sent when UATH_TX_NOTIFY is set */
"receive Tx notification\n");
break;
case WDCMSG_TARGET_GET_STATS:
"received device statistics\n");
break;
}
}
/* ARGSUSED */
static void
{
struct uath_cmd_hdr *hdr;
int len;
"cr:%s(%d), flags:0x%x, rx queued %d\n",
sc->rx_cmd_queued);
"USB CR is not OK\n");
goto fail;
}
/* Fragmented message, concatenate */
freemsg(m);
m = mp;
}
if (len < sizeof (struct uath_cmd_hdr)) {
"short xfer error\n");
goto fail;
}
else
(void) uath_rx_cmd_xfer(sc);
fail:
sc->rx_cmd_queued--;
if (m) freemsg(m);
}
static int
{
int err;
"failed to allocate req");
return (UATH_FAILURE);
}
req->bulk_timeout = 0;
req->bulk_completion_reason = 0;
req->bulk_cb_flags = 0;
if (err != USB_SUCCESS) {
"failed to do rx xfer, %d", err);
return (UATH_FAILURE);
}
sc->rx_cmd_queued++;
return (UATH_SUCCESS);
}
static void
{
}
/* ARGSUSED */
static void
{
"uath_txeof(): cr:%s(%d), flags:0x%x, tx_data_queued %d\n",
sc->tx_data_queued);
sc->tx_data_queued--;
if (sc->sc_need_sched) {
sc->sc_need_sched = 0;
}
}
static int
{
int err;
"uath_tx_data_xfer(): failed to allocate req");
return (UATH_FAILURE);
}
req->bulk_completion_reason = 0;
req->bulk_cb_flags = 0;
USB_SUCCESS) {
"failed to do tx xfer, %d", err);
return (UATH_FAILURE);
}
sc->tx_data_queued++;
return (UATH_SUCCESS);
}
/* ARGSUSED */
static void
{
struct uath_chunk *chunk;
struct uath_rx_desc *desc;
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
"cr:%s(%d), flags:0x%x, rx_data_queued %d\n",
sc->rx_data_queued);
"USB CR is not OK\n");
goto fail;
}
if (actlen < UATH_MIN_RXBUFSZ) {
"wrong recv size %d\n", actlen);
goto fail;
}
"strange response\n");
goto fail;
}
"invalid seqnum %d, expected %d\n",
goto fail;
}
/* check multi-chunk frames */
"receive multi-chunk frames "
"chunk seqnum %x, flags %x, length %u\n",
}
/* if the frame is not final continue the transfer */
sc->sc_intrx_nextnum++;
/*
* if the frame is not set UATH_CFLAGS_RXMSG, then rx descriptor is
* located at the end, 32-bit aligned
*/
sizeof (struct uath_rx_desc));
"frame len %u code %u status %u rate %u antenna %u "
"rssi %d channel %u phyerror %u connix %u "
"decrypterror %u keycachemiss %u\n",
goto fail;
}
"allocate mblk failed.\n");
sc->sc_rx_nobuf++;
goto fail;
}
/* send the frame to the 802.11 layer */
/* node is no longer needed */
fail:
sc->rx_data_queued--;
(void) uath_rx_data_xfer(sc);
}
}
static int
{
int err;
"failed to allocate req");
return (UATH_SUCCESS);
}
req->bulk_timeout = 0;
req->bulk_completion_reason = 0;
req->bulk_cb_flags = 0;
if (err != UATH_SUCCESS) {
"failed to do rx xfer, %d", err);
return (UATH_FAILURE);
}
sc->rx_data_queued++;
return (UATH_SUCCESS);
}
static void
{
switch (status) {
break;
case UATH_STATUS_CRC_ERR:
break;
case UATH_STATUS_PHY_ERR:
break;
break;
break;
case UATH_STATUS_DECOMP_ERR:
break;
case UATH_STATUS_KEY_ERR:
break;
case UATH_STATUS_ERR:
break;
default:
break;
}
}
static void
uath_next_scan(void *arg)
{
sc->sc_scan_id = 0;
}
static int
{
const struct ieee80211_rateset *rs;
struct uath_cmd_create_connection create;
int err;
/* XXX packed or not? */
/* XXX turbo */
else
sizeof (create), 0);
return (err);
}
static int
{
struct uath_cmd_rates rates;
int err;
/* XXX bounds check rs->rs_nrates */
return (err);
}
static int
{
struct uath_cmd_set_associd associd;
int err;
sizeof (associd), 0);
return (err);
}
static int
{
struct uath_cmd_ledsteady led;
int err;
"set %s led %s (steady)\n",
return (err);
}
static int
{
struct uath_cmd_ledblink led;
int err;
"set %s led %s (blink)\n",
return (err);
}
static int
{
enum ieee80211_state ostate;
int err;
if (sc->sc_scan_id != 0) {
sc->sc_scan_id = 0;
}
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
}
switch (nstate) {
case IEEE80211_S_INIT:
if (ostate == IEEE80211_S_RUN) {
/* turn link and activity LEDs off */
(void) uath_set_ledstate(sc, 0);
}
break;
case IEEE80211_S_SCAN:
"could not switch channel\n");
break;
}
drv_usectohz(250000));
break;
case IEEE80211_S_AUTH:
/* XXX good place? set RTS threshold */
"could not switch channel\n");
break;
}
"could not create connection\n");
break;
}
break;
case IEEE80211_S_ASSOC:
"could not set negotiated rate set\n");
break;
}
break;
case IEEE80211_S_RUN:
/* XXX monitor mode doesn't be supported */
break;
}
/*
* Tx rate is controlled by firmware, report the maximum
* negotiated rate in ifconfig output.
*/
if (uath_write_associd(sc) != 0) {
"could not write association id\n");
break;
}
/* turn link LED on */
/* make activity LED blink */
/* set state to associated */
break;
}
return (err);
}
static int
{
struct uath_chunk *chunk;
struct uath_tx_desc *desc;
struct ieee80211_frame *wh;
struct ieee80211_key *k;
err = UATH_SUCCESS;
if (UATH_IS_SUSPEND(sc)) {
err = 0;
goto fail;
}
"no TX buffer available!\n");
if ((type & IEEE80211_FC0_TYPE_MASK) ==
}
sc->sc_tx_nobuf++;
goto fail;
}
if (m == NULL) {
"can't alloc mblk.\n");
err = DDI_FAILURE;
goto fail;
}
/* skip TX descriptor */
}
err = DDI_FAILURE;
freemsg(m);
goto fail;
}
if ((type & IEEE80211_FC0_TYPE_MASK) ==
}
k = ieee80211_crypto_encap(ic, m);
if (k == NULL) {
freemsg(m);
err = DDI_FAILURE;
goto fail;
}
/* packet header may have moved, reset our local pointer */
}
/* one chunk only for now */
/* fill Tx descriptor */
/* NB: to get UATH_TX_NOTIFY reply, `msgid' must be larger than 0 */
case IEEE80211_FC0_TYPE_CTL:
case IEEE80211_FC0_TYPE_MGT:
/* NB: force all management frames to highest queue */
/* NB: force all management frames to highest queue */
} else
break;
case IEEE80211_FC0_TYPE_DATA:
/* XXX multicast frames should honor mcastrate */
break;
default:
"bogus frame type 0x%x (%s)\n",
goto fail;
}
else
(void) uath_tx_data_xfer(sc, m);
fail:
if ((mp) &&
err == 0)) {
}
return (err);
}
static int
{
struct uath_softc *sc;
struct ieee80211com *ic;
int err;
"uath online\n");
if (!UATH_IS_RECONNECT(sc)) {
if (err != UATH_SUCCESS) {
"could not open pipes\n");
return (DDI_FAILURE);
}
if (err != DDI_SUCCESS) {
"could not download firmware\n");
return (DDI_FAILURE);
}
/* reset device */
if (err != USB_SUCCESS) {
"could not reset device %x\n", err);
}
if (err != USB_SUCCESS) {
"usb_client_attach failed\n");
}
USB_PARSE_LVL_ALL, 0);
if (err != USB_SUCCESS) {
"usb_get_dev_data failed\n");
}
"HW does not match\n");
}
"vendorId = %x,deviceID = %x, flags = %x\n",
} else {
if (err != UATH_SUCCESS) {
"could not open pipes\n");
return (DDI_FAILURE);
}
/*
* Allocate xfers for firmware commands.
*/
if (err != UATH_SUCCESS) {
"could not allocate Tx command list\n");
return (DDI_FAILURE);
}
if (err != UATH_SUCCESS) {
"could not init RX command list\n");
return (DDI_FAILURE);
}
/*
* We're now ready to send+receive firmware commands.
*/
if (err != UATH_SUCCESS) {
"could not initialize adapter\n");
return (DDI_FAILURE);
}
if (err != UATH_SUCCESS) {
"could not get device capabilities\n");
return (DDI_FAILURE);
}
if (err != UATH_SUCCESS) {
"could not get dev status\n");
return (DDI_FAILURE);
}
if (err != USB_SUCCESS) {
"different device connected %x\n", err);
return (DDI_FAILURE);
}
if (err != UATH_SUCCESS) {
"device re-connect failed\n");
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static int
{
struct uath_softc *sc;
struct ieee80211com *ic;
/*
* We can't call uath_stop() here, since the hardware is removed,
* we can't access the register anymore.
*/
"stage 0 in re-connect\n");
return (DDI_SUCCESS);
}
/* abort and free xfers */
"offline success\n");
return (DDI_SUCCESS);
}
static int
{
struct uath_chunk *chunk;
struct uath_tx_desc *desc;
int err;
"no bufs\n");
return (ENOBUFS);
}
/* one chunk only */
sizeof (struct uath_chunk) + sizeof (struct uath_tx_desc));
if (err != UATH_SUCCESS) {
"data flush error");
return (UATH_FAILURE);
}
return (UATH_SUCCESS);
}
static int
{
}
static int
{
int err;
if (err != UATH_SUCCESS)
goto failed;
if (err != UATH_SUCCESS)
goto failed;
return (UATH_SUCCESS);
return (err);
}
static int
{
int err;
return (err);
}
static int
int len)
{
struct uath_write_mac write;
int err;
/* properly handle the case where len is zero (reset) */
if (err != UATH_SUCCESS) {
}
return (err);
}
static void
{
struct uath_write_mac write;
int err;
3 * sizeof (uint32_t), 0);
if (err != UATH_SUCCESS) {
"could not write register 0x%02x\n",
reg);
}
}
static int
{
int err;
/* set radio frequency */
if (err) {
"could not set channel\n");
goto failed;
}
/* reset Tx rings */
if (err) {
"could not reset Tx queues\n");
goto failed;
}
/* set Tx rings WME properties */
if (err) {
"could not init Tx queues\n");
goto failed;
}
if (err) {
"could not set led state\n");
goto failed;
}
if (err) {
"could not flush pipes\n");
goto failed;
}
return (err);
}
static int
{
struct uath_cmd_rx_filter rxfilter;
sizeof (rxfilter), 0)));
}
static int
{
struct uath_cmd_reset reset;
if (IEEE80211_IS_CHAN_2GHZ(c))
if (IEEE80211_IS_CHAN_5GHZ(c))
/* NB: 11g =>'s 11b so don't specify both OFDM and CCK */
if (UATH_IS_CHAN_OFDM(c))
else if (UATH_IS_CHAN_CCK(c))
/* turbo can be used in either 2GHz or 5GHz */
if (c->ich_flags & IEEE80211_CHAN_TURBO)
"set channel %d, flags 0x%x freq %u\n",
ieee80211_chan2ieee(ic, c),
}
static int
{
sizeof (qid), 0);
if (err != UATH_SUCCESS)
break;
}
return (err);
}
static int
{
/* XXX get from net80211 */
{ 7, 4, 10, 0, 0 }, /* Background */
{ 3, 4, 10, 0, 0 }, /* Best-Effort */
{ 3, 3, 4, 26, 0 }, /* Video */
{ 2, 2, 3, 47, 0 } /* Voice */
};
struct uath_cmd_txq_setup qinfo;
sizeof (qinfo), 0);
if (err != UATH_SUCCESS)
break;
}
return (err);
}
static void
uath_stop_locked(void *arg)
{
/* flush data & control requests into the target */
(void) uath_flush(sc);
/* set a LED status to the disconnected. */
(void) uath_set_ledstate(sc, 0);
/* stop the target */
/* abort any pending transfers */
}
static int
uath_init_locked(void *arg)
{
int i, err;
if (UATH_IS_RUNNING(sc))
/* reset variables */
/* set MAC address */
/* XXX honor net80211 state */
if (err) {
"could not start target\n");
goto fail;
}
"%s returns handle: 0x%x\n",
/* set default channel */
if (err) {
"could not switch channel, error %d\n", err);
goto fail;
}
/* XXX? check */
for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) {
if (err != UATH_SUCCESS) {
"could not alloc rx xfer %x\n", i);
goto fail;
}
}
/* enable Rx */
(void) uath_set_rxfilter(sc,
return (UATH_SUCCESS);
fail:
return (err);
}
static int
{
int err;
if (err != UATH_SUCCESS) {
"failed to initialize uath hardware\n");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
"uath stop now\n");
}
static void
{
int err;
"uath resume now\n");
/* check device changes after suspend */
"no or different device connected\n");
return;
}
/*
* initialize hardware
*/
if (err != UATH_SUCCESS) {
"could not init RX command list\n");
return;
}
if (err != UATH_SUCCESS) {
"hardware init failed\n");
return;
}
}
static int
uath_m_start(void *arg)
{
int err;
/*
* initialize hardware
*/
if (err != UATH_SUCCESS) {
"device configuration failed\n");
return (err);
}
return (DDI_SUCCESS);
}
static void
uath_m_stop(void *arg)
{
if (!UATH_IS_DISCONNECT(sc))
}
static void
{
int err;
if (ic->ic_des_esslen) {
if (UATH_IS_RUNNING(sc)) {
(void) ieee80211_new_state(ic,
IEEE80211_S_SCAN, -1);
}
}
}
}
/*ARGSUSED*/
static int
{
return (0);
}
/*ARGSUSED*/
static int
{
return (0);
}
/*ARGSUSED*/
static int
{
return (0);
}
/*
*/
static int
{
int err;
}
err = 0;
}
return (err);
}
static int
{
int err;
return (err);
}
static void
{
}
static int
{
switch (stat) {
case MAC_STAT_IFSPEED:
break;
case MAC_STAT_NOXMTBUF:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_OERRORS:
case WIFI_STAT_TX_FAILED:
break;
case WIFI_STAT_TX_RETRANS:
break;
case WIFI_STAT_FCS_ERRORS:
case WIFI_STAT_WEP_ERRORS:
case WIFI_STAT_TX_FRAGS:
case WIFI_STAT_MCAST_TX:
case WIFI_STAT_RTS_SUCCESS:
case WIFI_STAT_RTS_FAILURE:
case WIFI_STAT_ACK_FAILURE:
case WIFI_STAT_RX_FRAGS:
case WIFI_STAT_MCAST_RX:
case WIFI_STAT_RX_DUPS:
default:
return (ENOTSUP);
}
return (0);
}
static mblk_t *
{
/*
* No data frames go out unless we're associated; this
* should not happen as the 802.11 layer does not enable
* the xmit queue until we enter the RUN state.
*/
UATH_IS_SUSPEND(sc)) {
return (NULL);
}
break;
}
}
return (mp);
}
static int
{
struct uath_softc *sc;
struct ieee80211com *ic;
char strbuf[32];
wifi_data_t wd = { 0 };
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (err != DDI_SUCCESS) {
"ddi_soft_state_zalloc failed\n");
return (DDI_FAILURE);
}
if (err != USB_SUCCESS) {
"usb_client_attach failed\n");
goto fail1;
}
if (err != USB_SUCCESS) {
"usb_get_dev_data failed\n");
goto fail2;
}
"HW does not match\n");
goto fail2;
}
"vendorId = %x,deviceID = %x, flags = %x\n",
/*
* We must open the pipes early because they're used to upload the
* firmware (pre-firmware devices) or to send firmware commands.
*/
if (err != UATH_SUCCESS) {
"could not open pipes\n");
goto fail3;
}
if (err != DDI_SUCCESS) {
"could not read firmware %s, err %d\n",
"uath-ar5523", err);
goto fail3;
}
if (err != USB_SUCCESS) {
"could not re-attach, err %d\n", err);
goto fail1;
}
return (DDI_SUCCESS);
}
"firmware download and re-attach successfully\n");
/*
* Only post-firmware devices here.
*/
/*
* Allocate xfers for firmware commands.
*/
if (err != UATH_SUCCESS) {
"could not allocate Tx command list\n");
goto fail4;
}
if (err != UATH_SUCCESS) {
"could not init RX command list\n");
goto fail5;
}
/*
* We're now ready to send+receive firmware commands.
*/
if (err != UATH_SUCCESS) {
"could not initialize adapter\n");
goto fail5;
}
if (err != UATH_SUCCESS) {
"could not get device capabilities\n");
goto fail5;
}
if (err != UATH_SUCCESS) {
"could not get dev status\n");
goto fail5;
}
"MAC address is: %x:%x:%x:%x:%x:%x\n",
/* set device capabilities */
IEEE80211_C_TXPMGT | /* tx power management */
IEEE80211_C_SHPREAMBLE | /* short preamble supported */
IEEE80211_C_SHSLOT; /* short slot time supported */
/* set supported .11b and .11g rates */
/* set supported .11b and .11g channels (1 through 11) */
for (i = 1; i <= 11; i++) {
}
/* register WPA door */
/* override state transition machine */
ic->ic_def_txkey = 0;
/*
* Provide initial settings for the WiFi plugin; whenever this
* information changes, we need to call mac_plugindata_update()
*/
"MAC version mismatch\n");
goto fail5;
}
if (err != 0) {
"mac_register() error %x\n", err);
goto fail5;
};
if (err != USB_SUCCESS) {
"failed to register events\n");
goto fail6;
}
/*
* Create minor node of type DDI_NT_NET_WIFI
*/
"uath", instance);
if (err != DDI_SUCCESS)
"ddi_create_minor_node() failed\n");
/*
* Notify link is down now
*/
"attach success\n");
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static int
{
struct uath_softc *sc;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
if (UATH_IS_RUNNING(sc)) {
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
/*
* Unregister from the MAC layer subsystem
*/
return (DDI_FAILURE);
/*
* detach ieee80211 layer
*/
/* pipes will be close in uath_stop() */
return (DDI_SUCCESS);
}
int
{
}
int
_init(void)
{
int status;
sizeof (struct uath_softc), 1);
if (status != 0)
return (status);
if (status != 0) {
}
return (status);
}
int
_fini(void)
{
int status;
if (status == 0) {
}
return (status);
}