NetBSD/sys/dev/usb/if_atu.c
drochner 8258b792b7 Introduce different autoconf interface attributes for USB drivers
matching (and handling) a whole device and those which match an
interface only. This will allow to enforce some rules, eg that
the former don't use interface information for matching or that the
latter don't modify global device state.
The previous way left too much freedom do the drivers which led to
inconsistencies and abuse.
For now, I've not changed locators and submatch rules, this will
happen later.
There should not be any change in behaviour, except in the case of
some drivers which did behave inconsistently:
if_atu, if_axe, uep: matched the configured device in the interface
 stage, but did configuration again. I've converted them to match
 in the device stage.
ustir, utoppy: matched in the interface stage, but only against
 vendor/device information, and used any configuration/interface
 without checking. Changed to match in device stage, and added
 some simple code to configure and use the first interface.
If you have one of those devices, please test!
2007-03-13 13:51:53 +00:00

2221 lines
56 KiB
C

/* $NetBSD: if_atu.c,v 1.26 2007/03/13 13:51:54 drochner Exp $ */
/* $OpenBSD: if_atu.c,v 1.48 2004/12/30 01:53:21 dlg Exp $ */
/*
* 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
* documentation and/or other materials provided with the distribution.
* 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
* version 0.5 - 2004-08-03
*
* Originally written by Daan Vreeken <Danovitsch @ Vitsch . net>
* http://vitsch.net/bsd/atuwi
*
* 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.
* Ported to NetBSD by Jesse Off
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_atu.c,v 1.26 2007/03/13 13:51:54 drochner Exp $");
#include "bpfilter.h"
#include <sys/param.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kthread.h>
#include <sys/queue.h>
#include <sys/device.h>
#include <machine/bus.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbdivar.h>
#include <dev/usb/usbdevs.h>
#include <dev/microcode/atmel/atmel_intersil_fw.h>
#include <dev/microcode/atmel/atmel_rfmd2958-smc_fw.h>
#include <dev/microcode/atmel/atmel_rfmd2958_fw.h>
#include <dev/microcode/atmel/atmel_rfmd_fw.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_ether.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_radiotap.h>
#ifdef USB_DEBUG
#define ATU_DEBUG
#endif
#include <dev/usb/if_atureg.h>
#ifdef ATU_DEBUG
#define DPRINTF(x) do { if (atudebug) printf x; } while (0)
#define DPRINTFN(n,x) do { if (atudebug>(n)) printf x; } while (0)
int atudebug = 1;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
/*
* Various supported device vendors/products/radio type.
*/
struct atu_type atu_devs[] = {
{ USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_BW002,
RadioRFMD, ATU_NO_QUIRK },
{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D6050,
RadioRFMD, ATU_NO_QUIRK },
{ USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_AT76C503A,
RadioIntersil, ATU_NO_QUIRK },
{ USB_VENDOR_LEXAR, USB_PRODUCT_LEXAR_2662WAR,
RadioRFMD, ATU_NO_QUIRK },
/* Belkin F5D6050 */
{ USB_VENDOR_SMC3, USB_PRODUCT_SMC3_2662WUSB,
RadioRFMD, ATU_NO_QUIRK },
{ USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_WUSB11,
RadioRFMD, ATU_NO_QUIRK },
{ USB_VENDOR_LINKSYS3, USB_PRODUCT_LINKSYS3_WUSB11V28,
RadioRFMD2958, ATU_NO_QUIRK },
{ USB_VENDOR_NETGEAR2, USB_PRODUCT_NETGEAR2_MA101B,
RadioRFMD, ATU_NO_QUIRK },
{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_AWL400,
RadioRFMD, ATU_NO_QUIRK },
{ USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_WL1130,
RadioRFMD2958, ATU_NO_QUIRK },
{ USB_VENDOR_LINKSYS3, USB_PRODUCT_LINKSYS3_WUSB11V28,
RadioRFMD2958, ATU_NO_QUIRK },
{ USB_VENDOR_AINCOMM, USB_PRODUCT_AINCOMM_AWU2000B,
RadioRFMD2958, ATU_NO_QUIRK },
/* SMC2662 V.4 */
{ USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_AT76C505A,
RadioRFMD2958_SMC, ATU_QUIRK_NO_REMAP | ATU_QUIRK_FW_DELAY },
{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_AWL300,
RadioIntersil, ATU_NO_QUIRK },
{ USB_VENDOR_OQO, USB_PRODUCT_OQO_WIFI01,
RadioRFMD2958_SMC, ATU_QUIRK_NO_REMAP | ATU_QUIRK_FW_DELAY },
{ USB_VENDOR_SMC3, USB_PRODUCT_SMC3_2662WV1,
RadioIntersil, ATU_NO_QUIRK },
};
struct atu_radfirm {
enum atu_radio_type atur_type;
unsigned char *atur_internal;
size_t atur_internal_sz;
unsigned char *atur_external;
size_t atur_external_sz;
} atu_radfirm[] = {
{ RadioRFMD,
atmel_fw_rfmd_int, sizeof(atmel_fw_rfmd_int),
atmel_fw_rfmd_ext, sizeof(atmel_fw_rfmd_ext) },
{ RadioRFMD2958,
atmel_fw_rfmd2958_int, sizeof(atmel_fw_rfmd2958_int),
atmel_fw_rfmd2958_ext, sizeof(atmel_fw_rfmd2958_ext) },
{ RadioRFMD2958_SMC,
atmel_fw_rfmd2958_smc_int, sizeof(atmel_fw_rfmd2958_smc_int),
atmel_fw_rfmd2958_smc_ext, sizeof(atmel_fw_rfmd2958_smc_ext) },
{ RadioIntersil,
atmel_fw_intersil_int, sizeof(atmel_fw_intersil_int),
atmel_fw_intersil_ext, sizeof(atmel_fw_intersil_ext) }
};
int atu_newbuf(struct atu_softc *, struct atu_chain *, struct mbuf *);
void atu_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
void atu_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
void atu_start(struct ifnet *);
int atu_ioctl(struct ifnet *, u_long, void *);
int atu_init(struct ifnet *);
void atu_stop(struct ifnet *, int);
void atu_watchdog(struct ifnet *);
usbd_status atu_usb_request(struct atu_softc *sc, u_int8_t type,
u_int8_t request, u_int16_t value, u_int16_t index,
u_int16_t length, u_int8_t *data);
int atu_send_command(struct atu_softc *sc, u_int8_t *command, int size);
int atu_get_cmd_status(struct atu_softc *sc, u_int8_t cmd,
u_int8_t *status);
int atu_wait_completion(struct atu_softc *sc, u_int8_t cmd,
u_int8_t *status);
int atu_send_mib(struct atu_softc *sc, u_int8_t type,
u_int8_t size, u_int8_t index, void *data);
int atu_get_mib(struct atu_softc *sc, u_int8_t type,
u_int8_t size, u_int8_t index, u_int8_t *buf);
#if 0
int atu_start_ibss(struct atu_softc *sc);
#endif
int atu_start_scan(struct atu_softc *sc);
int atu_switch_radio(struct atu_softc *sc, int state);
int atu_initial_config(struct atu_softc *sc);
int atu_join(struct atu_softc *sc, struct ieee80211_node *node);
int8_t atu_get_dfu_state(struct atu_softc *sc);
u_int8_t atu_get_opmode(struct atu_softc *sc, u_int8_t *mode);
void atu_internal_firmware(struct device *);
void atu_external_firmware(struct device *);
int atu_get_card_config(struct atu_softc *sc);
int atu_media_change(struct ifnet *ifp);
void atu_media_status(struct ifnet *ifp, struct ifmediareq *req);
int atu_tx_list_init(struct atu_softc *);
int atu_rx_list_init(struct atu_softc *);
void atu_xfer_list_free(struct atu_softc *sc, struct atu_chain *ch,
int listlen);
#ifdef ATU_DEBUG
void atu_debug_print(struct atu_softc *sc);
#endif
void atu_task(void *);
int atu_newstate(struct ieee80211com *, enum ieee80211_state, int);
int atu_tx_start(struct atu_softc *, struct ieee80211_node *,
struct atu_chain *, struct mbuf *);
void atu_complete_attach(struct atu_softc *);
u_int8_t atu_calculate_padding(int);
USB_DECLARE_DRIVER(atu);
usbd_status
atu_usb_request(struct atu_softc *sc, u_int8_t type,
u_int8_t request, u_int16_t value, u_int16_t index, u_int16_t length,
u_int8_t *data)
{
usb_device_request_t req;
usbd_xfer_handle xfer;
usbd_status err;
int total_len = 0, s;
req.bmRequestType = type;
req.bRequest = request;
USETW(req.wValue, value);
USETW(req.wIndex, index);
USETW(req.wLength, length);
#ifdef ATU_DEBUG
if (atudebug) {
DPRINTFN(20, ("%s: req=%02x val=%02x ind=%02x "
"len=%02x\n", USBDEVNAME(sc->atu_dev), request,
value, index, length));
}
#endif /* ATU_DEBUG */
s = splnet();
xfer = usbd_alloc_xfer(sc->atu_udev);
usbd_setup_default_xfer(xfer, sc->atu_udev, 0, 500000, &req, data,
length, USBD_SHORT_XFER_OK, 0);
err = usbd_sync_transfer(xfer);
usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
#ifdef ATU_DEBUG
if (atudebug) {
if (type & UT_READ) {
DPRINTFN(20, ("%s: transfered 0x%x bytes in\n",
USBDEVNAME(sc->atu_dev), total_len));
} else {
if (total_len != length)
DPRINTF(("%s: wrote only %x bytes\n",
USBDEVNAME(sc->atu_dev), total_len));
}
}
#endif /* ATU_DEBUG */
usbd_free_xfer(xfer);
splx(s);
return(err);
}
int
atu_send_command(struct atu_softc *sc, u_int8_t *command, int size)
{
return atu_usb_request(sc, UT_WRITE_VENDOR_DEVICE, 0x0e, 0x0000,
0x0000, size, command);
}
int
atu_get_cmd_status(struct atu_softc *sc, u_int8_t cmd, u_int8_t *status)
{
/*
* 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 :)
*/
/*
return atu_usb_request(sc, UT_READ_VENDOR_INTERFACE, 0x22, cmd,
0x0000, 40, status);
*/
return atu_usb_request(sc, UT_READ_VENDOR_INTERFACE, 0x22, cmd,
0x0000, 6, status);
}
int
atu_wait_completion(struct atu_softc *sc, u_int8_t cmd, u_int8_t *status)
{
int idle_count = 0, err;
u_int8_t statusreq[6];
DPRINTFN(15, ("%s: wait-completion: cmd=%02x\n",
USBDEVNAME(sc->atu_dev), cmd));
while (1) {
err = atu_get_cmd_status(sc, cmd, statusreq);
if (err)
return err;
#ifdef ATU_DEBUG
if (atudebug) {
DPRINTFN(20, ("%s: status=%s cmd=%02x\n",
USBDEVNAME(sc->atu_dev),
ether_sprintf(statusreq), cmd));
}
#endif /* ATU_DEBUG */
/*
* during normal operations waiting on STATUS_IDLE
* will never happen more than once
*/
if ((statusreq[5] == STATUS_IDLE) && (idle_count++ > 20)) {
DPRINTF(("%s: idle_count > 20!\n",
USBDEVNAME(sc->atu_dev)));
return 0;
}
if ((statusreq[5] != STATUS_IN_PROGRESS) &&
(statusreq[5] != STATUS_IDLE)) {
if (status != NULL)
*status = statusreq[5];
return 0;
}
usbd_delay_ms(sc->atu_udev, 25);
}
}
int
atu_send_mib(struct atu_softc *sc, u_int8_t type, u_int8_t size,
u_int8_t index, void *data)
{
int err;
struct atu_cmd_set_mib request;
/*
* We don't construct a MIB packet first and then memcpy it into an
* Atmel-command-packet, we just construct it the right way at once :)
*/
memset(&request, 0, sizeof(request));
request.AtCmd = CMD_SET_MIB;
USETW(request.AtSize, size + 4);
request.MIBType = type;
request.MIBSize = size;
request.MIBIndex = index;
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:
request.data[0]=(long)data & 0x000000ff;
break;
case 2:
request.data[0]=(long)data & 0x000000ff;
request.data[1]=(long)data >> 8;
break;
default:
memcpy(request.data, data, size);
break;
}
err = atu_usb_request(sc, UT_WRITE_VENDOR_DEVICE, 0x0e, 0x0000,
0x0000, size+8, (uByte *)&request);
if (err)
return (err);
DPRINTFN(15, ("%s: sendmib : waitcompletion...\n",
USBDEVNAME(sc->atu_dev)));
return atu_wait_completion(sc, CMD_SET_MIB, NULL);
}
int
atu_get_mib(struct atu_softc *sc, u_int8_t type, u_int8_t size,
u_int8_t index, u_int8_t *buf)
{
/* linux/at76c503.c - 478 */
return atu_usb_request(sc, UT_READ_VENDOR_INTERFACE, 0x033,
type << 8, index, size, buf);
}
#if 0
int
atu_start_ibss(struct atu_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
int err;
struct atu_cmd_start_ibss Request;
Request.Cmd = CMD_START_IBSS;
Request.Reserved = 0;
Request.Size = sizeof(Request) - 4;
memset(Request.BSSID, 0x00, sizeof(Request.BSSID));
memset(Request.SSID, 0x00, sizeof(Request.SSID));
memcpy(Request.SSID, ic->ic_des_ssid, ic->ic_des_ssidlen);
Request.SSIDSize = ic->ic_des_ssidlen;
if (sc->atu_desired_channel != IEEE80211_CHAN_ANY)
Request.Channel = (u_int8_t)sc->atu_desired_channel;
else
Request.Channel = ATU_DEFAULT_CHANNEL;
Request.BSSType = AD_HOC_MODE;
memset(Request.Res, 0x00, sizeof(Request.Res));
/* Write config to adapter */
err = atu_send_command(sc, (u_int8_t *)&Request, sizeof(Request));
if (err) {
DPRINTF(("%s: start ibss failed!\n",
USBDEVNAME(sc->atu_dev)));
return err;
}
/* Wait for the adapter to do it's thing */
err = atu_wait_completion(sc, CMD_START_IBSS, NULL);
if (err) {
DPRINTF(("%s: error waiting for start_ibss\n",
USBDEVNAME(sc->atu_dev)));
return err;
}
/* Get the current BSSID */
err = atu_get_mib(sc, MIB_MAC_MGMT__CURRENT_BSSID, sc->atu_bssid);
if (err) {
DPRINTF(("%s: could not get BSSID!\n",
USBDEVNAME(sc->atu_dev)));
return err;
}
DPRINTF(("%s: started a new IBSS (BSSID=%s)\n",
USBDEVNAME(sc->atu_dev), ether_sprintf(sc->atu_bssid)));
return 0;
}
#endif
int
atu_start_scan(struct atu_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
struct atu_cmd_do_scan Scan;
usbd_status err;
int Cnt;
memset(&Scan, 0, sizeof(Scan));
Scan.Cmd = CMD_START_SCAN;
Scan.Reserved = 0;
USETW(Scan.Size, sizeof(Scan) - 4);
/* use the broadcast BSSID (in active scan) */
for (Cnt=0; Cnt<6; Cnt++)
Scan.BSSID[Cnt] = 0xff;
memset(Scan.SSID, 0x00, sizeof(Scan.SSID));
memcpy(Scan.SSID, ic->ic_des_essid, ic->ic_des_esslen);
Scan.SSID_Len = ic->ic_des_esslen;
/* default values for scan */
Scan.ScanType = ATU_SCAN_ACTIVE;
if (sc->atu_desired_channel != IEEE80211_CHAN_ANY)
Scan.Channel = (u_int8_t)sc->atu_desired_channel;
else
Scan.Channel = sc->atu_channel;
ic->ic_curchan = &ic->ic_channels[Scan.Channel];
/* we like scans to be quick :) */
/* the time we wait before sending probe's */
USETW(Scan.ProbeDelay, 0);
/* the time we stay on one channel */
USETW(Scan.MinChannelTime, 100);
USETW(Scan.MaxChannelTime, 200);
/* wether or not we scan all channels */
Scan.InternationalScan = 0xc1;
#ifdef ATU_DEBUG
if (atudebug) {
DPRINTFN(20, ("%s: scan cmd len=%02lx\n",
USBDEVNAME(sc->atu_dev), (unsigned long)sizeof(Scan)));
}
#endif /* ATU_DEBUG */
/* Write config to adapter */
err = atu_send_command(sc, (u_int8_t *)&Scan, sizeof(Scan));
if (err)
return err;
/*
* We don't wait for the command to finish... the mgmt-thread will do
* that for us
*/
/*
err = atu_wait_completion(sc, CMD_START_SCAN, NULL);
if (err)
return err;
*/
return 0;
}
int
atu_switch_radio(struct atu_softc *sc, int state)
{
usbd_status err;
struct atu_cmd CmdRadio;
if (sc->atu_radio == RadioIntersil) {
/*
* Intersil doesn't seem to need/support switching the radio
* on/off
*/
return 0;
}
memset(&CmdRadio, 0, sizeof(CmdRadio));
CmdRadio.Cmd = CMD_RADIO_ON;
if (sc->atu_radio_on != state) {
if (state == 0)
CmdRadio.Cmd = CMD_RADIO_OFF;
err = atu_send_command(sc, (u_int8_t *)&CmdRadio,
sizeof(CmdRadio));
if (err)
return err;
err = atu_wait_completion(sc, CmdRadio.Cmd, NULL);
if (err)
return err;
DPRINTFN(10, ("%s: radio turned %s\n",
USBDEVNAME(sc->atu_dev), state ? "on" : "off"));
sc->atu_radio_on = state;
}
return 0;
}
int
atu_initial_config(struct atu_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
u_int32_t i;
usbd_status err;
/* u_int8_t rates[4] = {0x82, 0x84, 0x8B, 0x96};*/
u_int8_t rates[4] = {0x82, 0x04, 0x0B, 0x16};
struct atu_cmd_card_config cmd;
u_int8_t reg_domain;
DPRINTFN(10, ("%s: sending mac-addr\n", USBDEVNAME(sc->atu_dev)));
err = atu_send_mib(sc, MIB_MAC_ADDR__ADDR, ic->ic_myaddr);
if (err) {
DPRINTF(("%s: error setting mac-addr\n",
USBDEVNAME(sc->atu_dev)));
return err;
}
/*
DPRINTF(("%s: sending reg-domain\n", USBDEVNAME(sc->atu_dev)));
err = atu_send_mib(sc, MIB_PHY__REG_DOMAIN, NR(0x30));
if (err) {
DPRINTF(("%s: error setting mac-addr\n",
USBDEVNAME(sc->atu_dev)));
return err;
}
*/
memset(&cmd, 0, sizeof(cmd));
cmd.Cmd = CMD_STARTUP;
cmd.Reserved = 0;
USETW(cmd.Size, sizeof(cmd) - 4);
if (sc->atu_desired_channel != IEEE80211_CHAN_ANY)
cmd.Channel = (u_int8_t)sc->atu_desired_channel;
else
cmd.Channel = sc->atu_channel;
cmd.AutoRateFallback = 1;
memcpy(cmd.BasicRateSet, rates, 4);
/* ShortRetryLimit should be 7 according to 802.11 spec */
cmd.ShortRetryLimit = 7;
USETW(cmd.RTS_Threshold, 2347);
USETW(cmd.FragThreshold, 2346);
/* Doesn't seem to work, but we'll set it to 1 anyway */
cmd.PromiscuousMode = 1;
/* this goes into the beacon we transmit */
if (ic->ic_flags & IEEE80211_F_PRIVACY)
cmd.PrivacyInvoked = 1;
else
cmd.PrivacyInvoked = 0;
cmd.ExcludeUnencrypted = 0;
if (ic->ic_flags & IEEE80211_F_PRIVACY) {
switch (ic->ic_nw_keys[ic->ic_def_txkey].wk_keylen) {
case 5:
cmd.EncryptionType = ATU_WEP_40BITS;
break;
case 13:
cmd.EncryptionType = ATU_WEP_104BITS;
break;
default:
cmd.EncryptionType = ATU_WEP_OFF;
break;
}
cmd.WEP_DefaultKeyID = ic->ic_def_txkey;
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
memcpy(cmd.WEP_DefaultKey[i], ic->ic_nw_keys[i].wk_key,
ic->ic_nw_keys[i].wk_keylen);
}
}
/* Setting the SSID here doesn't seem to do anything */
memset(cmd.SSID, 0x00, sizeof(cmd.SSID));
memcpy(cmd.SSID, ic->ic_des_essid, ic->ic_des_esslen);
cmd.SSID_Len = ic->ic_des_esslen;
cmd.ShortPreamble = 0;
USETW(cmd.BeaconPeriod, 100);
/* cmd.BeaconPeriod = 65535; */
/*
* TODO:
* read reg domain MIB_PHY @ 0x17 (1 byte), (reply = 0x30)
* we should do something usefull with this info. right now it's just
* ignored
*/
err = atu_get_mib(sc, MIB_PHY__REG_DOMAIN, &reg_domain);
if (err) {
DPRINTF(("%s: could not get regdomain!\n",
USBDEVNAME(sc->atu_dev)));
} else {
DPRINTF(("%s: in reg domain 0x%x according to the "
"adapter\n", USBDEVNAME(sc->atu_dev), reg_domain));
}
#ifdef ATU_DEBUG
if (atudebug) {
DPRINTFN(20, ("%s: configlen=%02lx\n", USBDEVNAME(sc->atu_dev),
(unsigned long)sizeof(cmd)));
}
#endif /* ATU_DEBUG */
/* Windoze : driver says exclude-unencrypted=1 & encr-type=1 */
err = atu_send_command(sc, (u_int8_t *)&cmd, sizeof(cmd));
if (err)
return err;
err = atu_wait_completion(sc, CMD_STARTUP, NULL);
if (err)
return err;
/* Turn on radio now */
err = atu_switch_radio(sc, 1);
if (err)
return err;
/* preamble type = short */
err = atu_send_mib(sc, MIB_LOCAL__PREAMBLE, NR(PREAMBLE_SHORT));
if (err)
return err;
/* frag = 1536 */
err = atu_send_mib(sc, MIB_MAC__FRAG, NR(2346));
if (err)
return err;
/* rts = 1536 */
err = atu_send_mib(sc, MIB_MAC__RTS, NR(2347));
if (err)
return err;
/* auto rate fallback = 1 */
err = atu_send_mib(sc, MIB_LOCAL__AUTO_RATE_FALLBACK, NR(1));
if (err)
return err;
/* power mode = full on, no power saving */
err = atu_send_mib(sc, MIB_MAC_MGMT__POWER_MODE,
NR(POWER_MODE_ACTIVE));
if (err)
return err;
DPRINTFN(10, ("%s: completed initial config\n",
USBDEVNAME(sc->atu_dev)));
return 0;
}
int
atu_join(struct atu_softc *sc, struct ieee80211_node *node)
{
struct atu_cmd_join join;
u_int8_t status = 0; /* XXX: GCC */
usbd_status err;
memset(&join, 0, sizeof(join));
join.Cmd = CMD_JOIN;
join.Reserved = 0x00;
USETW(join.Size, sizeof(join) - 4);
DPRINTFN(15, ("%s: pre-join sc->atu_bssid=%s\n",
USBDEVNAME(sc->atu_dev), ether_sprintf(sc->atu_bssid)));
DPRINTFN(15, ("%s: mode=%d\n", USBDEVNAME(sc->atu_dev),
sc->atu_mode));
memcpy(join.bssid, node->ni_bssid, IEEE80211_ADDR_LEN);
memset(join.essid, 0x00, 32);
memcpy(join.essid, node->ni_essid, node->ni_esslen);
join.essid_size = node->ni_esslen;
if (node->ni_capinfo & IEEE80211_CAPINFO_IBSS)
join.bss_type = AD_HOC_MODE;
else
join.bss_type = INFRASTRUCTURE_MODE;
join.channel = ieee80211_chan2ieee(&sc->sc_ic, node->ni_chan);
USETW(join.timeout, ATU_JOIN_TIMEOUT);
join.reserved = 0x00;
DPRINTFN(10, ("%s: trying to join BSSID=%s\n",
USBDEVNAME(sc->atu_dev), ether_sprintf(join.bssid)));
err = atu_send_command(sc, (u_int8_t *)&join, sizeof(join));
if (err) {
DPRINTF(("%s: ERROR trying to join IBSS\n",
USBDEVNAME(sc->atu_dev)));
return err;
}
err = atu_wait_completion(sc, CMD_JOIN, &status);
if (err) {
DPRINTF(("%s: error joining BSS!\n",
USBDEVNAME(sc->atu_dev)));
return err;
}
if (status != STATUS_COMPLETE) {
DPRINTF(("%s: error joining... [status=%02x]\n",
USBDEVNAME(sc->atu_dev), status));
return status;
} else {
DPRINTFN(10, ("%s: joined BSS\n", USBDEVNAME(sc->atu_dev)));
}
return err;
}
/*
* Get the state of the DFU unit
*/
int8_t
atu_get_dfu_state(struct atu_softc *sc)
{
u_int8_t state;
if (atu_usb_request(sc, DFU_GETSTATE, 0, 0, 1, &state))
return -1;
return state;
}
/*
* Get MAC opmode
*/
u_int8_t
atu_get_opmode(struct atu_softc *sc, u_int8_t *mode)
{
return atu_usb_request(sc, UT_READ_VENDOR_INTERFACE, 0x33, 0x0001,
0x0000, 1, mode);
}
/*
* Upload the internal firmware into the device
*/
void
atu_internal_firmware(struct device *arg)
{
struct atu_softc *sc = (struct atu_softc *)arg;
u_char state, *ptr = NULL, *firm = NULL, status[6];
int block_size, block = 0, err, i;
size_t bytes_left = 0;
/*
* 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
*/
/* Choose the right firmware for the device */
for (i = 0; i < sizeof(atu_radfirm)/sizeof(atu_radfirm[0]); i++)
if (sc->atu_radio == atu_radfirm[i].atur_type) {
firm = atu_radfirm[i].atur_internal;
bytes_left = atu_radfirm[i].atur_internal_sz;
}
if (firm == NULL) {
printf("%s: no firmware found\n", USBDEVNAME(sc->atu_dev));
return;
}
ptr = firm;
state = atu_get_dfu_state(sc);
while (block >= 0 && state > 0) {
switch (state) {
case DFUState_DnLoadSync:
/* get DFU status */
err = atu_usb_request(sc, DFU_GETSTATUS, 0, 0 , 6,
status);
if (err) {
DPRINTF(("%s: dfu_getstatus failed!\n",
USBDEVNAME(sc->atu_dev)));
return;
}
/* success means state => DnLoadIdle */
state = DFUState_DnLoadIdle;
continue;
break;
case DFUState_DFUIdle:
case DFUState_DnLoadIdle:
if (bytes_left>=DFU_MaxBlockSize)
block_size = DFU_MaxBlockSize;
else
block_size = bytes_left;
DPRINTFN(15, ("%s: firmware block %d\n",
USBDEVNAME(sc->atu_dev), block));
err = atu_usb_request(sc, DFU_DNLOAD, block++, 0,
block_size, ptr);
if (err) {
DPRINTF(("%s: dfu_dnload failed\n",
USBDEVNAME(sc->atu_dev)));
return;
}
ptr += block_size;
bytes_left -= block_size;
if (block_size == 0)
block = -1;
break;
default:
usbd_delay_ms(sc->atu_udev, 100);
DPRINTFN(20, ("%s: sleeping for a while\n",
USBDEVNAME(sc->atu_dev)));
break;
}
state = atu_get_dfu_state(sc);
}
if (state != DFUState_ManifestSync) {
DPRINTF(("%s: state != manifestsync... eek!\n",
USBDEVNAME(sc->atu_dev)));
}
err = atu_usb_request(sc, DFU_GETSTATUS, 0, 0, 6, status);
if (err) {
DPRINTF(("%s: dfu_getstatus failed!\n",
USBDEVNAME(sc->atu_dev)));
return;
}
DPRINTFN(15, ("%s: sending remap\n", USBDEVNAME(sc->atu_dev)));
err = atu_usb_request(sc, DFU_REMAP, 0, 0, 0, NULL);
if ((err) && (! sc->atu_quirk & ATU_QUIRK_NO_REMAP)) {
DPRINTF(("%s: remap failed!\n", USBDEVNAME(sc->atu_dev)));
return;
}
/* 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...
*/
usbd_delay_ms(sc->atu_udev, 56+100);
printf("%s: reattaching after firmware upload\n",
USBDEVNAME(sc->atu_dev));
usb_needs_reattach(sc->atu_udev);
}
void
atu_external_firmware(struct device *arg)
{
struct atu_softc *sc = (struct atu_softc *)arg;
u_char *ptr = NULL, *firm = NULL;
int block_size, block = 0, err, i;
size_t bytes_left = 0;
for (i = 0; i < sizeof(atu_radfirm)/sizeof(atu_radfirm[0]); i++)
if (sc->atu_radio == atu_radfirm[i].atur_type) {
firm = atu_radfirm[i].atur_external;
bytes_left = atu_radfirm[i].atur_external_sz;
}
if (firm == NULL) {
printf("%s: no firmware found\n", USBDEVNAME(sc->atu_dev));
return;
}
ptr = firm;
while (bytes_left) {
if (bytes_left > 1024)
block_size = 1024;
else
block_size = bytes_left;
DPRINTFN(15, ("%s: block:%d size:%d\n",
USBDEVNAME(sc->atu_dev), block, block_size));
err = atu_usb_request(sc, UT_WRITE_VENDOR_DEVICE, 0x0e,
0x0802, block, block_size, ptr);
if (err) {
DPRINTF(("%s: could not load external firmware "
"block\n", USBDEVNAME(sc->atu_dev)));
return;
}
ptr += block_size;
block++;
bytes_left -= block_size;
}
err = atu_usb_request(sc, UT_WRITE_VENDOR_DEVICE, 0x0e, 0x0802,
block, 0, NULL);
if (err) {
DPRINTF(("%s: could not load last zero-length firmware "
"block\n", USBDEVNAME(sc->atu_dev)));
return;
}
/*
* The SMC2662w V.4 seems to require some time to do it's thing with
* the external 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
*/
if (sc->atu_quirk & ATU_QUIRK_FW_DELAY)
usbd_delay_ms(sc->atu_udev, 21 + 100);
DPRINTFN(10, ("%s: external firmware upload done\n",
USBDEVNAME(sc->atu_dev)));
/* complete configuration after the firmwares have been uploaded */
atu_complete_attach(sc);
}
int
atu_get_card_config(struct atu_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
struct atu_rfmd_conf rfmd_conf;
struct atu_intersil_conf intersil_conf;
int err;
switch (sc->atu_radio) {
case RadioRFMD:
case RadioRFMD2958:
case RadioRFMD2958_SMC:
err = atu_usb_request(sc, UT_READ_VENDOR_INTERFACE, 0x33,
0x0a02, 0x0000, sizeof(rfmd_conf),
(u_int8_t *)&rfmd_conf);
if (err) {
DPRINTF(("%s: could not get rfmd config!\n",
USBDEVNAME(sc->atu_dev)));
return err;
}
memcpy(ic->ic_myaddr, rfmd_conf.MACAddr, IEEE80211_ADDR_LEN);
break;
case RadioIntersil:
err = atu_usb_request(sc, UT_READ_VENDOR_INTERFACE, 0x33,
0x0902, 0x0000, sizeof(intersil_conf),
(u_int8_t *)&intersil_conf);
if (err) {
DPRINTF(("%s: could not get intersil config!\n",
USBDEVNAME(sc->atu_dev)));
return err;
}
memcpy(ic->ic_myaddr, intersil_conf.MACAddr,
IEEE80211_ADDR_LEN);
break;
}
return 0;
}
/*
* Probe for an AT76c503 chip.
*/
USB_MATCH(atu)
{
USB_MATCH_START(atu, uaa);
int i;
for (i = 0; i < sizeof(atu_devs)/sizeof(atu_devs[0]); i++) {
struct atu_type *t = &atu_devs[i];
if (uaa->vendor == t->atu_vid &&
uaa->product == t->atu_pid) {
return(UMATCH_VENDOR_PRODUCT);
}
}
return(UMATCH_NONE);
}
int
atu_media_change(struct ifnet *ifp)
{
struct atu_softc *sc = ifp->if_softc;
struct ieee80211com *ic = &sc->sc_ic;
int err, s;
DPRINTFN(10, ("%s: atu_media_change\n", USBDEVNAME(sc->atu_dev)));
err = ieee80211_media_change(ifp);
if (err == ENETRESET) {
if ((ifp->if_flags & (IFF_RUNNING|IFF_UP)) ==
(IFF_RUNNING|IFF_UP)) {
s = splnet();
ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
atu_initial_config(sc);
ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
splx(s);
}
err = 0;
}
return (err);
}
void
atu_media_status(struct ifnet *ifp, struct ifmediareq *req)
{
#ifdef ATU_DEBUG
struct atu_softc *sc = ifp->if_softc;
#endif /* ATU_DEBUG */
DPRINTFN(10, ("%s: atu_media_status\n", USBDEVNAME(sc->atu_dev)));
ieee80211_media_status(ifp, req);
}
void
atu_task(void *arg)
{
struct atu_softc *sc = (struct atu_softc *)arg;
struct ieee80211com *ic = &sc->sc_ic;
usbd_status err;
int s;
DPRINTFN(10, ("%s: atu_task\n", USBDEVNAME(sc->atu_dev)));
if (sc->sc_state != ATU_S_OK)
return;
switch (sc->sc_cmd) {
case ATU_C_SCAN:
err = atu_start_scan(sc);
if (err) {
DPRINTFN(1, ("%s: atu_task: couldn't start scan!\n",
USBDEVNAME(sc->atu_dev)));
return;
}
err = atu_wait_completion(sc, CMD_START_SCAN, NULL);
if (err) {
DPRINTF(("%s: atu_task: error waiting for scan\n",
USBDEVNAME(sc->atu_dev)));
return;
}
DPRINTF(("%s: ==========================> END OF SCAN!\n",
USBDEVNAME(sc->atu_dev)));
s = splnet();
ieee80211_next_scan(ic);
splx(s);
DPRINTF(("%s: ----------------------======> END OF SCAN2!\n",
USBDEVNAME(sc->atu_dev)));
break;
case ATU_C_JOIN:
atu_join(sc, ic->ic_bss);
}
}
int
atu_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
{
struct ifnet *ifp = ic->ic_ifp;
struct atu_softc *sc = ifp->if_softc;
enum ieee80211_state ostate = ic->ic_state;
DPRINTFN(10, ("%s: atu_newstate: %s -> %s\n", USBDEVNAME(sc->atu_dev),
ieee80211_state_name[ostate], ieee80211_state_name[nstate]));
switch (nstate) {
case IEEE80211_S_SCAN:
memcpy(ic->ic_chan_scan, ic->ic_chan_active,
sizeof(ic->ic_chan_active));
ieee80211_node_table_reset(&ic->ic_scan);
/* tell the event thread that we want a scan */
sc->sc_cmd = ATU_C_SCAN;
usb_add_task(sc->atu_udev, &sc->sc_task, USB_TASKQ_DRIVER);
/* handle this ourselves */
ic->ic_state = nstate;
return (0);
case IEEE80211_S_AUTH:
case IEEE80211_S_RUN:
if (ostate == IEEE80211_S_SCAN) {
sc->sc_cmd = ATU_C_JOIN;
usb_add_task(sc->atu_udev, &sc->sc_task,
USB_TASKQ_DRIVER);
}
break;
default:
/* nothing to do */
break;
}
return (*sc->sc_newstate)(ic, nstate, arg);
}
/*
* Attach the interface. Allocate softc structures, do
* setup and ethernet/BPF attach.
*/
USB_ATTACH(atu)
{
USB_ATTACH_START(atu, sc, uaa);
char *devinfop;
usbd_status err;
usbd_device_handle dev = uaa->device;
u_int8_t mode, channel;
int i;
sc->sc_state = ATU_S_UNCONFIG;
devinfop = usbd_devinfo_alloc(dev, 0);
USB_ATTACH_SETUP;
printf("%s: %s\n", USBDEVNAME(sc->atu_dev), devinfop);
usbd_devinfo_free(devinfop);
err = usbd_set_config_no(dev, ATU_CONFIG_NO, 1);
if (err) {
printf("%s: setting config no failed\n",
USBDEVNAME(sc->atu_dev));
USB_ATTACH_ERROR_RETURN;
}
err = usbd_device2interface_handle(dev, ATU_IFACE_IDX, &sc->atu_iface);
if (err) {
printf("%s: getting interface handle failed\n",
USBDEVNAME(sc->atu_dev));
USB_ATTACH_ERROR_RETURN;
}
sc->atu_unit = device_unit(self);
sc->atu_udev = dev;
/*
* look up the radio_type for the device
* basically does the same as USB_MATCH
*/
for (i = 0; i < sizeof(atu_devs)/sizeof(atu_devs[0]); i++) {
struct atu_type *t = &atu_devs[i];
if (uaa->vendor == t->atu_vid &&
uaa->product == t->atu_pid) {
sc->atu_radio = t->atu_radio;
sc->atu_quirk = t->atu_quirk;
}
}
/*
* Check in the interface descriptor if we're in DFU mode
* If we're in DFU mode, we upload the external firmware
* If we're not, the PC must have rebooted without power-cycling
* the device.. I've tried this out, a reboot only requeres the
* external firmware to be reloaded :)
*
* Hmm. The at76c505a doesn't report a DFU descriptor when it's
* in DFU mode... Let's just try to get the opmode
*/
err = atu_get_opmode(sc, &mode);
DPRINTFN(20, ("%s: opmode: %d\n", USBDEVNAME(sc->atu_dev), mode));
if (err || (mode != MODE_NETCARD && mode != MODE_NOFLASHNETCARD)) {
DPRINTF(("%s: starting internal firmware download\n",
USBDEVNAME(sc->atu_dev)));
atu_internal_firmware((struct device *)sc);
/*
* atu_internal_firmware will cause a reset of the device
* so we don't want to do any more configuration after this
* point.
*/
USB_ATTACH_SUCCESS_RETURN;
}
if (mode != MODE_NETCARD) {
DPRINTFN(15, ("%s: device needs external firmware\n",
USBDEVNAME(sc->atu_dev)));
if (mode != MODE_NOFLASHNETCARD) {
DPRINTF(("%s: unexpected opmode=%d\n",
USBDEVNAME(sc->atu_dev), mode));
}
/*
* There is no difference in opmode before and after external
* firmware upload with the SMC2662 V.4 . So instead we'll try
* to read the channel number. If we succeed, external
* firmwaremust have been already uploaded...
*/
if (sc->atu_radio != RadioIntersil) {
err = atu_get_mib(sc, MIB_PHY__CHANNEL, &channel);
if (!err) {
DPRINTF(("%s: external firmware has already"
" been downloaded\n",
USBDEVNAME(sc->atu_dev)));
atu_complete_attach(sc);
USB_ATTACH_SUCCESS_RETURN;
}
}
atu_external_firmware((struct device *)sc);
/*
* atu_external_firmware will call atu_complete_attach after
* it's finished so we can just return.
*/
} else {
/* all the firmwares are in place, so complete the attach */
atu_complete_attach(sc);
}
USB_ATTACH_SUCCESS_RETURN;
}
void
atu_complete_attach(struct atu_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = &sc->sc_if;
usb_interface_descriptor_t *id;
usb_endpoint_descriptor_t *ed;
usbd_status err;
int i;
#ifdef ATU_DEBUG
struct atu_fw fw;
#endif
id = usbd_get_interface_descriptor(sc->atu_iface);
/* Find endpoints. */
for (i = 0; i < id->bNumEndpoints; i++) {
ed = usbd_interface2endpoint_descriptor(sc->atu_iface, i);
if (!ed) {
DPRINTF(("%s: num_endp:%d\n", USBDEVNAME(sc->atu_dev),
sc->atu_iface->idesc->bNumEndpoints));
DPRINTF(("%s: couldn't get ep %d\n",
USBDEVNAME(sc->atu_dev), i));
return;
}
if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
sc->atu_ed[ATU_ENDPT_RX] = ed->bEndpointAddress;
} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
sc->atu_ed[ATU_ENDPT_TX] = ed->bEndpointAddress;
}
}
/* read device config & get MAC address */
err = atu_get_card_config(sc);
if (err) {
printf("\n%s: could not get card cfg!\n",
USBDEVNAME(sc->atu_dev));
return;
}
#ifdef ATU_DEBUG
/* DEBUG : try to get firmware version */
err = atu_get_mib(sc, MIB_FW_VERSION, sizeof(fw), 0,
(u_int8_t *)&fw);
if (!err) {
DPRINTFN(15, ("%s: firmware: maj:%d min:%d patch:%d "
"build:%d\n", USBDEVNAME(sc->atu_dev), fw.major, fw.minor,
fw.patch, fw.build));
} else {
DPRINTF(("%s: get firmware version failed\n",
USBDEVNAME(sc->atu_dev)));
}
#endif /* ATU_DEBUG */
/* Show the world our MAC address */
printf("%s: MAC address %s\n", USBDEVNAME(sc->atu_dev),
ether_sprintf(ic->ic_myaddr));
sc->atu_cdata.atu_tx_inuse = 0;
sc->atu_encrypt = ATU_WEP_OFF;
sc->atu_wepkeylen = ATU_WEP_104BITS;
sc->atu_wepkey = 0;
bzero(sc->atu_bssid, ETHER_ADDR_LEN);
sc->atu_channel = ATU_DEFAULT_CHANNEL;
sc->atu_desired_channel = IEEE80211_CHAN_ANY;
sc->atu_mode = INFRASTRUCTURE_MODE;
ic->ic_ifp = ifp;
ic->ic_phytype = IEEE80211_T_DS;
ic->ic_opmode = IEEE80211_M_STA;
ic->ic_state = IEEE80211_S_INIT;
#ifdef FIXME
ic->ic_caps = IEEE80211_C_IBSS | IEEE80211_C_WEP | IEEE80211_C_SCANALL;
#else
ic->ic_caps = IEEE80211_C_IBSS | IEEE80211_C_WEP;
#endif
i = 0;
ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[i++] = 2;
ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[i++] = 4;
ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[i++] = 11;
ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[i++] = 22;
ic->ic_sup_rates[IEEE80211_MODE_11B].rs_nrates = i;
for (i = 1; i <= 14; i++) {
ic->ic_channels[i].ic_flags = IEEE80211_CHAN_B |
IEEE80211_CHAN_PASSIVE;
ic->ic_channels[i].ic_freq = ieee80211_ieee2mhz(i,
ic->ic_channels[i].ic_flags);
}
ic->ic_ibss_chan = &ic->ic_channels[0];
ifp->if_softc = sc;
memcpy(ifp->if_xname, USBDEVNAME(sc->atu_dev), IFNAMSIZ);
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_init = atu_init;
ifp->if_stop = atu_stop;
ifp->if_start = atu_start;
ifp->if_ioctl = atu_ioctl;
ifp->if_watchdog = atu_watchdog;
ifp->if_mtu = ATU_DEFAULT_MTU;
IFQ_SET_READY(&ifp->if_snd);
/* Call MI attach routine. */
if_attach(ifp);
ieee80211_ifattach(ic);
sc->sc_newstate = ic->ic_newstate;
ic->ic_newstate = atu_newstate;
/* setup ifmedia interface */
ieee80211_media_init(ic, atu_media_change, atu_media_status);
usb_init_task(&sc->sc_task, atu_task, sc);
sc->sc_state = ATU_S_OK;
}
USB_DETACH(atu)
{
USB_DETACH_START(atu, sc);
struct ifnet *ifp = &sc->sc_if;
DPRINTFN(10, ("%s: atu_detach state=%d\n", USBDEVNAME(sc->atu_dev),
sc->sc_state));
if (sc->sc_state != ATU_S_UNCONFIG) {
atu_stop(ifp, 1);
ieee80211_ifdetach(&sc->sc_ic);
if_detach(ifp);
}
return(0);
}
int
atu_activate(device_ptr_t self, enum devact act)
{
struct atu_softc *sc = (struct atu_softc *)self;
switch (act) {
case DVACT_ACTIVATE:
return (EOPNOTSUPP);
break;
case DVACT_DEACTIVATE:
if (sc->sc_state != ATU_S_UNCONFIG) {
if_deactivate(&sc->atu_ec.ec_if);
sc->sc_state = ATU_S_DEAD;
}
break;
}
return (0);
}
/*
* Initialize an RX descriptor and attach an MBUF cluster.
*/
int
atu_newbuf(struct atu_softc *sc, struct atu_chain *c, struct mbuf *m)
{
struct mbuf *m_new = NULL;
if (m == NULL) {
MGETHDR(m_new, M_DONTWAIT, MT_DATA);
if (m_new == NULL) {
DPRINTF(("%s: no memory for rx list\n",
USBDEVNAME(sc->atu_dev)));
return(ENOBUFS);
}
MCLGET(m_new, M_DONTWAIT);
if (!(m_new->m_flags & M_EXT)) {
DPRINTF(("%s: no memory for rx list\n",
USBDEVNAME(sc->atu_dev)));
m_freem(m_new);
return(ENOBUFS);
}
m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
} else {
m_new = m;
m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
m_new->m_data = m_new->m_ext.ext_buf;
}
c->atu_mbuf = m_new;
return(0);
}
int
atu_rx_list_init(struct atu_softc *sc)
{
struct atu_cdata *cd = &sc->atu_cdata;
struct atu_chain *c;
int i;
DPRINTFN(15, ("%s: atu_rx_list_init: enter\n",
USBDEVNAME(sc->atu_dev)));
for (i = 0; i < ATU_RX_LIST_CNT; i++) {
c = &cd->atu_rx_chain[i];
c->atu_sc = sc;
c->atu_idx = i;
if (c->atu_xfer == NULL) {
c->atu_xfer = usbd_alloc_xfer(sc->atu_udev);
if (c->atu_xfer == NULL)
return (ENOBUFS);
c->atu_buf = usbd_alloc_buffer(c->atu_xfer,
ATU_RX_BUFSZ);
if (c->atu_buf == NULL) /* XXX free xfer */
return (ENOBUFS);
if (atu_newbuf(sc, c, NULL) == ENOBUFS) /* XXX free? */
return(ENOBUFS);
}
}
return (0);
}
int
atu_tx_list_init(struct atu_softc *sc)
{
struct atu_cdata *cd = &sc->atu_cdata;
struct atu_chain *c;
int i;
DPRINTFN(15, ("%s: atu_tx_list_init\n",
USBDEVNAME(sc->atu_dev)));
SLIST_INIT(&cd->atu_tx_free);
sc->atu_cdata.atu_tx_inuse = 0;
for (i = 0; i < ATU_TX_LIST_CNT; i++) {
c = &cd->atu_tx_chain[i];
c->atu_sc = sc;
c->atu_idx = i;
if (c->atu_xfer == NULL) {
c->atu_xfer = usbd_alloc_xfer(sc->atu_udev);
if (c->atu_xfer == NULL)
return(ENOBUFS);
c->atu_mbuf = NULL;
c->atu_buf = usbd_alloc_buffer(c->atu_xfer,
ATU_TX_BUFSZ);
if (c->atu_buf == NULL)
return(ENOBUFS); /* XXX free xfer */
SLIST_INSERT_HEAD(&cd->atu_tx_free, c, atu_list);
}
}
return(0);
}
void
atu_xfer_list_free(struct atu_softc *sc, struct atu_chain *ch,
int listlen)
{
int i;
/* Free resources. */
for (i = 0; i < listlen; i++) {
if (ch[i].atu_buf != NULL)
ch[i].atu_buf = NULL;
if (ch[i].atu_mbuf != NULL) {
m_freem(ch[i].atu_mbuf);
ch[i].atu_mbuf = NULL;
}
if (ch[i].atu_xfer != NULL) {
usbd_free_xfer(ch[i].atu_xfer);
ch[i].atu_xfer = NULL;
}
}
}
/*
* A frame has been uploaded: pass the resulting mbuf chain up to
* the higher level protocols.
*/
void
atu_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
struct atu_chain *c = (struct atu_chain *)priv;
struct atu_softc *sc = c->atu_sc;
struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = &sc->sc_if;
struct atu_rx_hdr *h;
struct ieee80211_frame_min *wh;
struct ieee80211_node *ni;
struct mbuf *m;
u_int32_t len;
int s;
DPRINTFN(25, ("%s: atu_rxeof\n", USBDEVNAME(sc->atu_dev)));
if (sc->sc_state != ATU_S_OK)
return;
if ((ifp->if_flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP))
goto done;
if (status != USBD_NORMAL_COMPLETION) {
DPRINTF(("%s: status != USBD_NORMAL_COMPLETION\n",
USBDEVNAME(sc->atu_dev)));
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
return;
}
#if 0
if (status == USBD_IOERROR) {
DPRINTF(("%s: rx: EEK! lost device?\n",
USBDEVNAME(sc->atu_dev)));
/*
* My experience with USBD_IOERROR is that trying to
* restart the transfer will always fail and we'll
* keep on looping restarting transfers untill someone
* pulls the plug of the device.
* So we don't restart the transfer, but just let it
* die... If someone knows of a situation where we can
* recover from USBD_IOERROR, let me know.
*/
splx(s);
return;
}
#endif /* 0 */
if (usbd_ratecheck(&sc->atu_rx_notice)) {
DPRINTF(("%s: usb error on rx: %s\n",
USBDEVNAME(sc->atu_dev), usbd_errstr(status)));
}
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(
sc->atu_ep[ATU_ENDPT_RX]);
goto done;
}
usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
if (len <= 1) {
DPRINTF(("%s: atu_rxeof: too short\n",
USBDEVNAME(sc->atu_dev)));
goto done;
}
h = (struct atu_rx_hdr *)c->atu_buf;
len = UGETW(h->length) - 4; /* XXX magic number */
m = c->atu_mbuf;
memcpy(mtod(m, char *), c->atu_buf + ATU_RX_HDRLEN, len);
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = len;
wh = mtod(m, struct ieee80211_frame_min *);
ni = ieee80211_find_rxnode(ic, wh);
ifp->if_ipackets++;
s = splnet();
if (atu_newbuf(sc, c, NULL) == ENOBUFS) {
ifp->if_ierrors++;
goto done1; /* XXX if we can't allocate, why restart it? */
}
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
/*
* WEP is decrypted by hardware. Clear WEP bit
* header for ieee80211_input().
*/
wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
}
ieee80211_input(ic, m, ni, h->rssi, UGETDW(h->rx_time));
ieee80211_free_node(ni);
done1:
splx(s);
done:
/* Setup new transfer. */
usbd_setup_xfer(c->atu_xfer, sc->atu_ep[ATU_ENDPT_RX], c, c->atu_buf,
ATU_RX_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
atu_rxeof);
usbd_transfer(c->atu_xfer);
}
/*
* A frame was downloaded to the chip. It's safe for us to clean up
* the list buffers.
*/
void
atu_txeof(usbd_xfer_handle xfer, usbd_private_handle priv,
usbd_status status)
{
struct atu_chain *c = (struct atu_chain *)priv;
struct atu_softc *sc = c->atu_sc;
struct ifnet *ifp = &sc->sc_if;
usbd_status err;
int s;
DPRINTFN(25, ("%s: atu_txeof status=%d\n", USBDEVNAME(sc->atu_dev),
status));
if (c->atu_mbuf) {
m_freem(c->atu_mbuf);
c->atu_mbuf = NULL;
}
if (status != USBD_NORMAL_COMPLETION) {
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
return;
DPRINTF(("%s: usb error on tx: %s\n", USBDEVNAME(sc->atu_dev),
usbd_errstr(status)));
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc->atu_ep[ATU_ENDPT_TX]);
return;
}
usbd_get_xfer_status(c->atu_xfer, NULL, NULL, NULL, &err);
if (err)
ifp->if_oerrors++;
else
ifp->if_opackets++;
s = splnet();
SLIST_INSERT_HEAD(&sc->atu_cdata.atu_tx_free, c, atu_list);
sc->atu_cdata.atu_tx_inuse--;
if (sc->atu_cdata.atu_tx_inuse == 0)
ifp->if_timer = 0;
ifp->if_flags &= ~IFF_OACTIVE;
splx(s);
atu_start(ifp);
}
u_int8_t
atu_calculate_padding(int size)
{
size %= 64;
if (size < 50)
return (50 - size);
if (size >=61)
return (64 + 50 - size);
return (0);
}
int
atu_tx_start(struct atu_softc *sc, struct ieee80211_node *ni,
struct atu_chain *c, struct mbuf *m)
{
int len;
struct atu_tx_hdr *h;
usbd_status err;
u_int8_t pad;
DPRINTFN(25, ("%s: atu_tx_start\n", USBDEVNAME(sc->atu_dev)));
/* Don't try to send when we're shutting down the driver */
if (sc->sc_state != ATU_S_OK) {
m_freem(m);
return(EIO);
}
/*
* Copy the mbuf data into a contiguous buffer, leaving
* enough room for the atmel headers
*/
len = m->m_pkthdr.len;
m_copydata(m, 0, m->m_pkthdr.len, c->atu_buf + ATU_TX_HDRLEN);
h = (struct atu_tx_hdr *)c->atu_buf;
memset(h, 0, ATU_TX_HDRLEN);
USETW(h->length, len);
h->tx_rate = 4; /* XXX rate = auto */
len += ATU_TX_HDRLEN;
pad = atu_calculate_padding(len);
len += pad;
h->padding = pad;
c->atu_length = len;
c->atu_mbuf = m;
usbd_setup_xfer(c->atu_xfer, sc->atu_ep[ATU_ENDPT_TX],
c, c->atu_buf, c->atu_length, USBD_NO_COPY, ATU_TX_TIMEOUT,
atu_txeof);
/* Let's get this thing into the air! */
c->atu_in_xfer = 1;
err = usbd_transfer(c->atu_xfer);
if (err != USBD_IN_PROGRESS) {
DPRINTFN(25, ("%s: atu_tx_start, err=%d",
USBDEVNAME(sc->atu_dev), err));
c->atu_mbuf = NULL;
m_freem(m);
return(EIO);
}
return (0);
}
void
atu_start(struct ifnet *ifp)
{
struct atu_softc *sc = ifp->if_softc;
struct ieee80211com *ic = &sc->sc_ic;
struct atu_cdata *cd = &sc->atu_cdata;
struct ieee80211_node *ni;
struct atu_chain *c;
struct mbuf *m = NULL;
int s;
DPRINTFN(25, ("%s: atu_start: enter\n", USBDEVNAME(sc->atu_dev)));
if ((ifp->if_flags & IFF_RUNNING) == 0) {
return;
}
if (ifp->if_flags & IFF_OACTIVE) {
DPRINTFN(30, ("%s: atu_start: IFF_OACTIVE\n",
USBDEVNAME(sc->atu_dev)));
return;
}
for (;;) {
/* grab a TX buffer */
s = splnet();
c = SLIST_FIRST(&cd->atu_tx_free);
if (c != NULL) {
SLIST_REMOVE_HEAD(&cd->atu_tx_free, atu_list);
cd->atu_tx_inuse++;
if (cd->atu_tx_inuse == ATU_TX_LIST_CNT)
ifp->if_flags |= IFF_OACTIVE;
}
splx(s);
if (c == NULL) {
DPRINTFN(10, ("%s: out of tx xfers\n",
USBDEVNAME(sc->atu_dev)));
ifp->if_flags |= IFF_OACTIVE;
break;
}
/*
* Poll the management queue for frames, it has priority over
* normal data frames.
*/
IF_DEQUEUE(&ic->ic_mgtq, m);
if (m == NULL) {
DPRINTFN(10, ("%s: atu_start: data packet\n",
USBDEVNAME(sc->atu_dev)));
if (ic->ic_state != IEEE80211_S_RUN) {
DPRINTFN(25, ("%s: no data till running\n",
USBDEVNAME(sc->atu_dev)));
/* put the xfer back on the list */
s = splnet();
SLIST_INSERT_HEAD(&cd->atu_tx_free, c,
atu_list);
cd->atu_tx_inuse--;
splx(s);
break;
}
IFQ_DEQUEUE(&ifp->if_snd, m);
if (m == NULL) {
DPRINTFN(25, ("%s: nothing to send\n",
USBDEVNAME(sc->atu_dev)));
s = splnet();
SLIST_INSERT_HEAD(&cd->atu_tx_free, c,
atu_list);
cd->atu_tx_inuse--;
splx(s);
break;
}
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m);
#endif
ni = ieee80211_find_txnode(ic,
mtod(m, struct ether_header *)->ether_dhost);
if (ni == NULL) {
m_freem(m);
goto bad;
}
m = ieee80211_encap(ic, m, ni);
if (m == NULL)
goto bad;
} else {
DPRINTFN(25, ("%s: atu_start: mgmt packet\n",
USBDEVNAME(sc->atu_dev)));
/*
* Hack! The referenced node pointer is in the
* rcvif field of the packet header. This is
* placed there by ieee80211_mgmt_output because
* we need to hold the reference with the frame
* and there's no other way (other than packet
* tags which we consider too expensive to use)
* to pass it along.
*/
ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
m->m_pkthdr.rcvif = NULL;
/* sc->sc_stats.ast_tx_mgmt++; */
}
#if NBPFILTER > 0
if (ic->ic_rawbpf)
bpf_mtap(ic->ic_rawbpf, m);
#endif
if (atu_tx_start(sc, ni, c, m)) {
bad:
s = splnet();
SLIST_INSERT_HEAD(&cd->atu_tx_free, c,
atu_list);
cd->atu_tx_inuse--;
splx(s);
/* ifp_if_oerrors++; */
if (ni != NULL)
ieee80211_free_node(ni);
continue;
}
ifp->if_timer = 5;
}
}
int
atu_init(struct ifnet *ifp)
{
struct atu_softc *sc = ifp->if_softc;
struct ieee80211com *ic = &sc->sc_ic;
struct atu_chain *c;
usbd_status err;
int i, s;
s = splnet();
DPRINTFN(10, ("%s: atu_init\n", USBDEVNAME(sc->atu_dev)));
if (ifp->if_flags & IFF_RUNNING) {
splx(s);
return(0);
}
/* Init TX ring */
if (atu_tx_list_init(sc))
printf("%s: tx list init failed\n", USBDEVNAME(sc->atu_dev));
/* Init RX ring */
if (atu_rx_list_init(sc))
printf("%s: rx list init failed\n", USBDEVNAME(sc->atu_dev));
/* Load the multicast filter. */
/*atu_setmulti(sc); */
/* Open RX and TX pipes. */
err = usbd_open_pipe(sc->atu_iface, sc->atu_ed[ATU_ENDPT_RX],
USBD_EXCLUSIVE_USE, &sc->atu_ep[ATU_ENDPT_RX]);
if (err) {
DPRINTF(("%s: open rx pipe failed: %s\n",
USBDEVNAME(sc->atu_dev), usbd_errstr(err)));
splx(s);
return(EIO);
}
err = usbd_open_pipe(sc->atu_iface, sc->atu_ed[ATU_ENDPT_TX],
USBD_EXCLUSIVE_USE, &sc->atu_ep[ATU_ENDPT_TX]);
if (err) {
DPRINTF(("%s: open tx pipe failed: %s\n",
USBDEVNAME(sc->atu_dev), usbd_errstr(err)));
splx(s);
return(EIO);
}
/* Start up the receive pipe. */
for (i = 0; i < ATU_RX_LIST_CNT; i++) {
c = &sc->atu_cdata.atu_rx_chain[i];
usbd_setup_xfer(c->atu_xfer, sc->atu_ep[ATU_ENDPT_RX], c,
c->atu_buf, ATU_RX_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
USBD_NO_TIMEOUT, atu_rxeof);
usbd_transfer(c->atu_xfer);
}
DPRINTFN(10, ("%s: starting up using MAC=%s\n",
USBDEVNAME(sc->atu_dev), ether_sprintf(ic->ic_myaddr)));
/* Do initial setup */
err = atu_initial_config(sc);
if (err) {
DPRINTF(("%s: initial config failed!\n",
USBDEVNAME(sc->atu_dev)));
splx(s);
return(EIO);
}
DPRINTFN(10, ("%s: initialised transceiver\n",
USBDEVNAME(sc->atu_dev)));
/* sc->atu_rxfilt = ATU_RXFILT_UNICAST|ATU_RXFILT_BROADCAST; */
/* If we want promiscuous mode, set the allframes bit. */
/*
if (ifp->if_flags & IFF_PROMISC)
sc->atu_rxfilt |= ATU_RXFILT_PROMISC;
*/
ifp->if_flags |= IFF_RUNNING;
ifp->if_flags &= ~IFF_OACTIVE;
splx(s);
/* XXX the following HAS to be replaced */
s = splnet();
err = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
if (err) {
DPRINTFN(1, ("%s: atu_init: error calling "
"ieee80211_net_state", USBDEVNAME(sc->atu_dev)));
}
splx(s);
return 0;
}
#ifdef ATU_DEBUG
void
atu_debug_print(struct atu_softc *sc)
{
usbd_status err;
u_int8_t tmp[32];
/* DEBUG */
if ((err = atu_get_mib(sc, MIB_MAC_MGMT__CURRENT_BSSID, tmp)))
return;
DPRINTF(("%s: DEBUG: current BSSID=%s\n", USBDEVNAME(sc->atu_dev),
ether_sprintf(tmp)));
if ((err = atu_get_mib(sc, MIB_MAC_MGMT__BEACON_PERIOD, tmp)))
return;
DPRINTF(("%s: DEBUG: beacon period=%d\n", USBDEVNAME(sc->atu_dev),
tmp[0]));
if ((err = atu_get_mib(sc, MIB_MAC_WEP__PRIVACY_INVOKED, tmp)))
return;
DPRINTF(("%s: DEBUG: privacy invoked=%d\n", USBDEVNAME(sc->atu_dev),
tmp[0]));
if ((err = atu_get_mib(sc, MIB_MAC_WEP__ENCR_LEVEL, tmp)))
return;
DPRINTF(("%s: DEBUG: encr_level=%d\n", USBDEVNAME(sc->atu_dev),
tmp[0]));
if ((err = atu_get_mib(sc, MIB_MAC_WEP__ICV_ERROR_COUNT, tmp)))
return;
DPRINTF(("%s: DEBUG: icv error count=%d\n", USBDEVNAME(sc->atu_dev),
*(short *)tmp));
if ((err = atu_get_mib(sc, MIB_MAC_WEP__EXCLUDED_COUNT, tmp)))
return;
DPRINTF(("%s: DEBUG: wep excluded count=%d\n",
USBDEVNAME(sc->atu_dev), *(short *)tmp));
if ((err = atu_get_mib(sc, MIB_MAC_MGMT__POWER_MODE, tmp)))
return;
DPRINTF(("%s: DEBUG: power mode=%d\n", USBDEVNAME(sc->atu_dev),
tmp[0]));
if ((err = atu_get_mib(sc, MIB_PHY__CHANNEL, tmp)))
return;
DPRINTF(("%s: DEBUG: channel=%d\n", USBDEVNAME(sc->atu_dev), tmp[0]));
if ((err = atu_get_mib(sc, MIB_PHY__REG_DOMAIN, tmp)))
return;
DPRINTF(("%s: DEBUG: reg domain=%d\n", USBDEVNAME(sc->atu_dev),
tmp[0]));
if ((err = atu_get_mib(sc, MIB_LOCAL__SSID_SIZE, tmp)))
return;
DPRINTF(("%s: DEBUG: ssid size=%d\n", USBDEVNAME(sc->atu_dev),
tmp[0]));
if ((err = atu_get_mib(sc, MIB_LOCAL__BEACON_ENABLE, tmp)))
return;
DPRINTF(("%s: DEBUG: beacon enable=%d\n", USBDEVNAME(sc->atu_dev),
tmp[0]));
if ((err = atu_get_mib(sc, MIB_LOCAL__AUTO_RATE_FALLBACK, tmp)))
return;
DPRINTF(("%s: DEBUG: auto rate fallback=%d\n",
USBDEVNAME(sc->atu_dev), tmp[0]));
if ((err = atu_get_mib(sc, MIB_MAC_ADDR__ADDR, tmp)))
return;
DPRINTF(("%s: DEBUG: mac addr=%s\n", USBDEVNAME(sc->atu_dev),
ether_sprintf(tmp)));
if ((err = atu_get_mib(sc, MIB_MAC__DESIRED_SSID, tmp)))
return;
DPRINTF(("%s: DEBUG: desired ssid=%s\n", USBDEVNAME(sc->atu_dev),
tmp));
if ((err = atu_get_mib(sc, MIB_MAC_MGMT__CURRENT_ESSID, tmp)))
return;
DPRINTF(("%s: DEBUG: current ESSID=%s\n", USBDEVNAME(sc->atu_dev),
tmp));
}
#endif /* ATU_DEBUG */
int
atu_ioctl(struct ifnet *ifp, u_long command, void *data)
{
struct atu_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
struct ieee80211com *ic = &sc->sc_ic;
int err = 0, s;
s = splnet();
switch (command) {
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
err = ifmedia_ioctl(ifp, ifr, &ic->ic_media, command);
break;
default:
DPRINTFN(15, ("%s: ieee80211_ioctl (%lu)\n",
USBDEVNAME(sc->atu_dev), command));
err = ieee80211_ioctl(ic, command, data);
break;
}
if (err == ENETRESET) {
if ((ifp->if_flags & (IFF_RUNNING|IFF_UP)) ==
(IFF_RUNNING|IFF_UP)) {
DPRINTF(("%s: atu_ioctl(): netreset %lu\n",
USBDEVNAME(sc->atu_dev), command));
ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
atu_initial_config(sc);
ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
}
err = 0;
}
splx(s);
return (err);
}
void
atu_watchdog(struct ifnet *ifp)
{
struct atu_softc *sc = ifp->if_softc;
struct atu_chain *c;
usbd_status stat;
int cnt, s;
DPRINTF(("%s: atu_watchdog\n", USBDEVNAME(sc->atu_dev)));
ifp->if_timer = 0;
if (sc->sc_state != ATU_S_OK || (ifp->if_flags & IFF_RUNNING) == 0)
return;
sc = ifp->if_softc;
s = splnet();
ifp->if_oerrors++;
DPRINTF(("%s: watchdog timeout\n", USBDEVNAME(sc->atu_dev)));
/*
* TODO:
* we should change this since we have multiple TX tranfers...
*/
for (cnt = 0; cnt < ATU_TX_LIST_CNT; cnt++) {
c = &sc->atu_cdata.atu_tx_chain[cnt];
if (c->atu_in_xfer) {
usbd_get_xfer_status(c->atu_xfer, NULL, NULL, NULL,
&stat);
atu_txeof(c->atu_xfer, c, stat);
}
}
if (!IFQ_IS_EMPTY(&ifp->if_snd))
atu_start(ifp);
splx(s);
ieee80211_watchdog(&sc->sc_ic);
}
/*
* Stop the adapter and free any mbufs allocated to the
* RX and TX lists.
*/
void
atu_stop(struct ifnet *ifp, int disable)
{
struct atu_softc *sc = ifp->if_softc;
struct ieee80211com *ic = &sc->sc_ic;
struct atu_cdata *cd;
usbd_status err;
int s;
s = splnet();
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
ifp->if_timer = 0;
usb_rem_task(sc->atu_udev, &sc->sc_task);
ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
/* Stop transfers. */
if (sc->atu_ep[ATU_ENDPT_RX] != NULL) {
err = usbd_abort_pipe(sc->atu_ep[ATU_ENDPT_RX]);
if (err) {
DPRINTF(("%s: abort rx pipe failed: %s\n",
USBDEVNAME(sc->atu_dev), usbd_errstr(err)));
}
err = usbd_close_pipe(sc->atu_ep[ATU_ENDPT_RX]);
if (err) {
DPRINTF(("%s: close rx pipe failed: %s\n",
USBDEVNAME(sc->atu_dev), usbd_errstr(err)));
}
sc->atu_ep[ATU_ENDPT_RX] = NULL;
}
if (sc->atu_ep[ATU_ENDPT_TX] != NULL) {
err = usbd_abort_pipe(sc->atu_ep[ATU_ENDPT_TX]);
if (err) {
DPRINTF(("%s: abort tx pipe failed: %s\n",
USBDEVNAME(sc->atu_dev), usbd_errstr(err)));
}
err = usbd_close_pipe(sc->atu_ep[ATU_ENDPT_TX]);
if (err) {
DPRINTF(("%s: close tx pipe failed: %s\n",
USBDEVNAME(sc->atu_dev), usbd_errstr(err)));
}
sc->atu_ep[ATU_ENDPT_TX] = NULL;
}
/* Free RX/TX/MGMT list resources. */
cd = &sc->atu_cdata;
atu_xfer_list_free(sc, cd->atu_rx_chain, ATU_RX_LIST_CNT);
atu_xfer_list_free(sc, cd->atu_tx_chain, ATU_TX_LIST_CNT);
/* Let's be nice and turn off the radio before we leave */
atu_switch_radio(sc, 0);
splx(s);
}