/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2003, 2004
* Daan Vreeken <Danovitsch@Vitsch.net>. 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.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daan Vreeken.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Daan Vreeken AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Daan Vreeken OR THE VOICES IN HIS HEAD
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE.
*/
/*
* Atmel AT76c503 / AT76c503a / AT76c505 / AT76c505a USB WLAN driver
*
* Originally written by Daan Vreeken <Danovitsch @ Vitsch . net>
*
* Contributed to by :
* Chris Whitehouse, Alistair Phillips, Peter Pilka, Martijn van Buul,
* Suihong Liang, Arjan van Leeuwen, Stuart Walsh
*
* Ported to OpenBSD by Theo de Raadt and David Gwynne.
*/
#include <sys/mac_provider.h>
#include <sys/mac_wifi.h>
#include <sys/net80211.h>
#define USBDRV_MINOR_VER 0
#include "fw/atmel_rfmd.hex"
#include "fw/atmel_rfmd2958.hex"
#include "fw/atmel_rfmd2958-smc.hex"
#include "fw/atmel_intersil.hex"
#include "fw/atmel_at76c505_rfmd.hex"
#include "fw/atmel_at76c503_rfmd_acc.hex"
#include "fw/atmel_at76c503_i3863.hex"
#include "atu.h"
static void *atu_soft_state_p;
static int
{
if (type & USB_DEV_REQ_DEV_TO_HOST) {
return (EIO);
if (uret == USB_SUCCESS)
} else {
return (ENOMEM);
}
if (mp)
}
static int
{
}
static int
{
/*
* all other drivers (including Windoze) request 40 bytes of status
* and get a short-xfer of just 6 bytes. we can save 34 bytes of
* buffer if we just request those 6 bytes in the first place :)
*/
}
static uint8_t
{
return (DFUState_DFUError);
return (state);
}
static int
{
}
static int
{
int err;
case RadioRFMD:
case RadioRFMD2958:
case RadioRFMD2958_SMC:
case AT76C503_RFMD_ACC:
case AT76C505_RFMD:
if (err) {
return (err);
}
break;
case RadioIntersil:
case AT76C503_i3863:
if (err) {
return (err);
}
break;
}
return (0);
}
static int
{
return (0);
} else if (idle_count++ > 60) {
return (ETIME);
}
}
return (err);
}
static int
{
}
static int
{
int err;
request.MIBReserved = 0;
/*
* For 1 and 2 byte requests we assume a direct value,
* everything bigger than 2 bytes we assume a pointer to the data
*/
switch (size) {
case 0:
break;
case 1:
break;
case 2:
break;
default:
break;
}
if (err)
return (err);
}
static int
{
int err;
/* Intersil doesn't seem to support radio switch */
return (0);
sizeof (radio));
if (err)
return (err);
if (err)
return (err);
if (on)
else
}
return (0);
}
static int
{
struct ieee80211_key *k;
int err, i;
if (err) {
return (err);
}
switch (k->wk_keylen) {
case 5:
break;
case 13:
break;
default:
goto nowep;
}
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
k = ic->ic_nw_keys + i;
if (k->wk_keylen == 0)
continue;
}
} else {
}
if (err)
return (err);
if (err)
return (err);
if (err)
return (err);
(void *)ATU_POWER_ACTIVE);
if (err)
return (err);
return (0);
}
static int
{
int err;
if (!ATU_RUNNING(sc))
return (EIO);
scan.ProbeDelay = 0;
scan.InternationalScan = 0;
if (err) {
return (err);
}
if (err) {
return (err);
}
return (0);
}
static int
{
int err;
else
if (err) {
return (err);
}
if (err)
return (err);
if (status != STATUS_COMPLETE) {
return (EIO);
}
return (0);
}
static int
{
int err = 0;
if (sc->sc_scan_timer != 0) {
ATU_UNLOCK(sc);
sc->sc_scan_timer = 0;
}
switch (nstate) {
case IEEE80211_S_SCAN:
switch (ostate) {
case IEEE80211_S_SCAN:
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
case IEEE80211_S_RUN:
ATU_UNLOCK(sc);
ATU_UNLOCK(sc);
return (err);
}
(void (*) (void*))ieee80211_next_scan,
ATU_UNLOCK(sc);
return (err);
default:
break;
}
break;
case IEEE80211_S_AUTH:
switch (ostate) {
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
if (err) {
ATU_UNLOCK(sc);
return (err);
}
break;
default:
break;
}
default:
break;
}
ATU_UNLOCK(sc);
return (err);
}
static int
{
int uret;
if (uret != USB_SUCCESS)
goto fail;
if (uret != USB_SUCCESS)
goto fail;
return (0);
fail:
USB_FLAGS_SLEEP, NULL, 0);
}
USB_FLAGS_SLEEP, NULL, 0);
}
return (EIO);
}
static void
{
}
}
}
/*ARGSUSED*/
static void
{
struct atu_rx_hdr *h;
goto fail;
}
goto fail;
}
goto fail;
}
done:
if (ATU_RUNNING(sc))
(void) atu_rx_trigger(sc);
return;
fail:
goto done;
}
/*ARGSUSED*/
static void
{
if (sc->sc_need_sched) {
sc->sc_need_sched = 0;
}
}
static int
{
int uret;
return (ENOMEM);
req->bulk_timeout = 0;
req->bulk_completion_reason = 0;
req->bulk_cb_flags = 0;
if (uret != USB_SUCCESS) {
return (EIO);
}
return (0);
}
static int
{
int uret;
return (EIO);
req->bulk_completion_reason = 0;
req->bulk_cb_flags = 0;
if (uret != USB_SUCCESS) {
return (EIO);
}
return (0);
}
static int
{
int err, i;
for (i = 0; i < ATU_RX_LIST_CNT; i++) {
if (err)
return (err);
}
return (0);
}
static void
{
}
static int
{
mblk_t *m;
sc->sc_tx_nobuf++;
goto fail;
}
if (m == NULL) {
sc->sc_tx_nobuf++;
goto fail;
}
/* reserve tx header space */
m->b_rptr += ATU_TX_HDRLEN;
m->b_wptr += ATU_TX_HDRLEN;
/* copy and (implicitly) free old data */
freemsg(m);
goto fail;
}
if (type == IEEE80211_FC0_TYPE_DATA)
/* full WEP in device, prune WEP fields (IV, KID) */
+ IEEE80211_WEP_KIDLEN, m->b_rptr,
sizeof (struct ieee80211_frame));
}
m->b_rptr -= ATU_TX_HDRLEN;
/* setup tx header */
if (!err) {
} else {
freemsg(m);
}
fail:
return (err);
}
static int
{
}
static int
{
int err;
if (err)
return (err);
if (err)
goto fail;
if (err) {
goto fail;
}
if (err) {
goto fail;
}
return (0);
fail:
return (err);
}
static void
{
if (!ATU_RUNNING(sc)) {
ATU_UNLOCK(sc);
return;
}
ATU_UNLOCK(sc);
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
else
break;
}
}
static int
{
/*
* Uploading firmware is done with the DFU (Device Firmware Upgrade)
* interface. See "Universal Serial Bus - Device Class Specification
* for Device Firmware Upgrade" pdf for details of the protocol.
* Maybe this could be moved to a separate 'firmware driver' once more
* device drivers need it... For now we'll just do it here.
*
* Just for your information, the Atmel's DFU descriptor looks like
* this:
*
* 07 size
* 21 type
* 01 capabilities : only firmware download, *need* reset
* after download
* 13 05 detach timeout : max 1299ms between DFU_DETACH and
* reset
* 00 04 max bytes of firmware per transaction : 1024
*/
for (i = 0; i < sizeof (atu_fw_table) / sizeof (atu_fw_table[0]); i++)
}
switch (state) {
case DFUState_DnLoadSync:
/* get DFU status */
status);
if (err) {
return (err);
}
/* success means state => DnLoadIdle */
continue;
case DFUState_DFUIdle:
case DFUState_DnLoadIdle:
if (bytes_left >= DFU_MaxBlockSize)
else
block_size, ptr);
if (err) {
return (err);
}
ptr += block_size;
bytes_left -= block_size;
if (block_size == 0)
block = -1;
break;
case DFUState_DFUError:
return (EIO);
default:
if (++count > 100) {
return (ETIME);
}
break;
}
}
if (state != DFUState_ManifestSync)
if (err) {
return (err);
}
return (err);
}
/*
* after a lot of trying and measuring I found out the device needs
* about 56 miliseconds after sending the remap command before
* it's ready to communicate again. So we'll wait just a little bit
* longer than that to be sure...
*/
return (0);
}
static int
{
for (i = 0; i < sizeof (atu_fw_table) / sizeof (atu_fw_table[0]); i++)
}
while (bytes_left) {
if (bytes_left > 1024)
block_size = 1024;
else
if (err) {
return (err);
}
ptr += block_size;
block++;
bytes_left -= block_size;
}
if (err) {
return (err);
}
/*
* The SMC2662w V.4 seems to require some time to do its thing with
* the stage2 firmware... 20 ms isn't enough, but 21 ms works 100
* times out of 100 tries. We'll wait a bit longer just to be sure
*/
return (0);
}
static int
{
int err;
if (!err) {
if (mode == ATU_DEV_READY)
return (0);
/*
* Opmode of SMC2662 V.4 does not change after stage2
* firmware download. If succeeded reading the channel
* number, stage2 firmware is already running.
*/
return (0);
if (mode == ATU_DEV_STAGE2)
return (atu_dfu_stage2(sc));
}
if (err)
return (err);
return (EIO);
if (attach)
return (EAGAIN);
else
goto stage2;
}
static int
{
if (sc->sc_scan_timer != 0) {
ATU_UNLOCK(sc);
sc->sc_scan_timer = 0;
}
ATU_UNLOCK(sc);
return (0);
}
static int
{
int err;
return (DDI_FAILURE);
if (!err)
ATU_UNLOCK(sc);
}
static int
{
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
"atu", instance);
if (err != USB_SUCCESS)
goto fail1;
if (err != USB_SUCCESS) {
goto fail2;
}
for (i = 0; i < sizeof (atu_dev_table)/sizeof (atu_dev_table[0]); i++) {
struct atu_dev_type *t = &atu_dev_table[i];
}
}
return (DDI_SUCCESS);
} else if (err) {
goto fail2;
}
/* read device config & MAC address */
if (err) {
goto fail2;
}
for (i = 1; i <= 14; i++) {
}
ic->ic_def_txkey = 0;
goto fail3;
if (err)
goto fail3;
if (err != USB_SUCCESS)
goto fail4;
if (err != DDI_SUCCESS)
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static int
{
int err;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
if (sc->sc_scan_timer != 0) {
ATU_UNLOCK(sc);
sc->sc_scan_timer = 0;
}
ATU_UNLOCK(sc);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (!ATU_REATTACH(sc)) {
if (err)
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
"atu driver v1.1",
};
(void *)&atu_modldrv,
};
int
{
}
int
_init(void)
{
int status;
sizeof (struct atu_softc), 1);
if (status != 0)
return (status);
if (status != 0) {
}
return (status);
}
int
_fini(void)
{
int status;
if (status == 0) {
}
return (status);
}
static int
{
int err;
ATU_UNLOCK(sc);
return (err);
}
static void
{
if (sc->sc_scan_timer != 0) {
ATU_UNLOCK(sc);
sc->sc_scan_timer = 0;
}
ATU_UNLOCK(sc);
}
/*ARGSUSED*/
static int
{
return (ENOTSUP);
}
/*ARGSUSED*/
static int
{
return (ENOTSUP);
}
/*ARGSUSED*/
static int
{
return (0);
}
static int
const void *buf)
{
int err;
return (err);
if (ic->ic_des_esslen == 0)
return (0);
if (ATU_RUNNING(sc)) {
if (sc->sc_scan_timer != 0) {
ATU_UNLOCK(sc);
sc->sc_scan_timer = 0;
}
ATU_UNLOCK(sc);
if (err)
return (err);
}
ATU_UNLOCK(sc);
return (0);
}
static int
{
}
static void
{
}
static void
{
int err;
return;
if (ATU_RUNNING(sc)) {
if (sc->sc_scan_timer != 0) {
ATU_UNLOCK(sc);
sc->sc_scan_timer = 0;
}
ATU_UNLOCK(sc);
if (err)
return;
}
ATU_UNLOCK(sc);
}
static mblk_t *
{
return (NULL);
}
return (mp);
}
}
return (mp);
}
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:
break;
case WIFI_STAT_TX_FRAGS:
case WIFI_STAT_MCAST_TX:
case WIFI_STAT_TX_FAILED:
case WIFI_STAT_TX_RETRANS:
case WIFI_STAT_TX_RERETRANS:
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_FCS_ERRORS:
case WIFI_STAT_WEP_ERRORS:
case WIFI_STAT_RX_DUPS:
ATU_UNLOCK(sc);
default:
ATU_UNLOCK(sc);
return (ENOTSUP);
}
ATU_UNLOCK(sc);
return (0);
}
NULL,
NULL,
NULL,
NULL,
};