diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/usb/files.usb | 7 | ||||
-rw-r--r-- | sys/dev/usb/if_atu.c | 3596 | ||||
-rw-r--r-- | sys/dev/usb/if_atureg.h | 723 |
3 files changed, 4325 insertions, 1 deletions
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb index e0ef61ecdcc..a8cb26f3576 100644 --- a/sys/dev/usb/files.usb +++ b/sys/dev/usb/files.usb @@ -1,4 +1,4 @@ -# $OpenBSD: files.usb,v 1.37 2004/10/05 02:01:18 jsg Exp $ +# $OpenBSD: files.usb,v 1.38 2004/11/08 22:09:11 dlg Exp $ # $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $ # # Config file and device description for machine-independent USB code. @@ -207,3 +207,8 @@ file dev/usb/usscanner.c usscanner # Prism3 WI @ USB attach wi at uhub with wi_usb file dev/usb/if_wi_usb.c wi_usb + +# Atmel AT76c503 / AT76c503a / AT76c505 / AT76c505a @ USB +device atu +attach atu at uhub +file dev/usb/if_atu.c atu diff --git a/sys/dev/usb/if_atu.c b/sys/dev/usb/if_atu.c new file mode 100644 index 00000000000..ab8ff8823f3 --- /dev/null +++ b/sys/dev/usb/if_atu.c @@ -0,0 +1,3596 @@ +/* $OpenBSD: if_atu.c,v 1.1 2004/11/08 22:09:11 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/atu + * + * 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/param.h> +#include <sys/systm.h> +#include <sys/lock.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/socket.h> + +#include <sys/sockio.h> +#include <sys/malloc.h> +#include <sys/kernel.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> + +#include "bpfilter.h" +#define BPF_MTAP(ifp, m) bpf_mtap((ifp)->if_bpf, (m)) +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#include <sys/ucred.h> +#include <sys/kthread.h> +#include <sys/queue.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> +#if 0 +#include <dev/usb/usb_ethersubr.h> +#endif + +#if 0 +#include <dev/ic/anreg.h> +#endif + +#include <dev/usb/usbdevs.h> + +#include <dev/ic/if_wi_ieee.h> + +#include <net80211/ieee80211.h> +#include <net80211/ieee80211_ioctl.h> +#include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_var.h> + +#include <dev/usb/if_atureg.h> + +#include <dev/microcode/atmel/atuwi_rfmd_fw.h> +#include <dev/microcode/atmel/atuwi_rfmd2958_fw.h> +#include <dev/microcode/atmel/atuwi_rfmd2958-smc_fw.h> +#include <dev/microcode/atmel/atuwi_intersil_fw.h> + +#ifdef USB_DEBUG +#define ATU_DEBUG +#endif + +#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 + +USB_DECLARE_DRIVER_CLASS(atu, DV_IFNET); + +/* + * 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_LEXAR, USB_PRODUCT_LEXAR_2662WAR, + RadioRFMD, ATU_NO_QUIRK }, + { USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_WUSB11, + RadioRFMD, 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_ATMEL, USB_PRODUCT_ATMEL_DWL120, + RadioIntersil, ATU_NO_QUIRK }, + { USB_VENDOR_ACERP, USB_PRODUCT_ACERP_AWL300, + RadioIntersil, ATU_NO_QUIRK }, + { 0, 0, 0, 0 } +}; + +int atu_newbuf(struct atu_softc *, struct atu_chain *, struct mbuf *); +int atu_encap(struct atu_softc *sc, struct mbuf *m, struct atu_chain *c); +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 *); +// void atu_rxstart(struct ifnet *); +void atu_mgmt_loop(void *arg); +int atu_ioctl(struct ifnet *, u_long, caddr_t); +void atu_init(void *); +void atu_stop(struct atu_softc *); +void atu_watchdog(struct ifnet *); +void atu_msleep(struct atu_softc *, int); +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); +int atu_start_ibss(struct atu_softc *sc); +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); +#if 0 +void +atu_airo_tap(struct atu_softc *sc, u_int8_t *pkt, u_int length, + struct at76c503_rx_buffer *at_hdr); +#endif +int +atu_send_packet(struct atu_softc *sc, struct atu_chain *c); +int atu_send_mgmt_packet(struct atu_softc *sc, + struct atu_chain *c, u_int16_t length); +int atu_authenticate(struct atu_softc *sc); +int atu_associate(struct atu_softc *sc); +int8_t atu_get_dfu_state(struct atu_softc *sc); +u_int8_t atu_get_opmode(struct atu_softc *sc, u_int8_t *mode); +int atu_upload_internal_firmware(struct atu_softc *sc); +int atu_upload_external_firmware(struct atu_softc *sc); +int atu_get_card_config(struct atu_softc *sc); +int atu_mgmt_state_machine(struct atu_softc *sc); +int atu_media_change(struct ifnet *ifp); +void atu_media_status(struct ifnet *ifp, struct ifmediareq *req); +int atu_xfer_list_init(struct atu_softc *sc, struct atu_chain *ch, + int listlen, int need_mbuf, int bufsize, struct atu_list_head *list); +void atu_xfer_list_free(struct atu_softc *sc, struct atu_chain *ch, + int listlen); +void atu_print_beacon(struct atu_softc *sc, struct atu_rxpkt *pkt); +void atu_handle_mgmt_packet(struct atu_softc *sc, struct atu_rxpkt *pkt); +void atu_print_a_bunch_of_debug_things(struct atu_softc *sc); +int atu_set_wepkey(struct atu_softc *sc, int nr, u_int8_t *key, int len); + + +void +atu_msleep(struct atu_softc *sc, int ms) +{ + u_int8_t dummy; + int ticks; + + usbd_delay_ms(sc->atu_udev, ms); + return; + + ticks = ms * hz / 1000; + if (ticks == 0) + ticks = 1; + + tsleep(&dummy, PZERO | PCATCH, "atus", ms * hz / 1000); +} + +static usbd_status +atu_reset(struct atu_softc *sc) +{ + /* We don't need to actually send the device a reset... */ +#if 0 + usb_port_status_t stat; + + usbd_reset_port(sc->atu_udev->myhub, + sc->atu_udev->powersrc->portno, &stat); +#endif + + sc->atu_udev->address = USB_START_ADDR; + return(0); +} + +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) { + if ((data == NULL) || (type & UT_READ)) { + DPRINTFN(10, ("%s: req=%02x val=%02x " + "ind=%02x len=%02x\n", USBDEVNAME(sc->atu_dev), + request, value, index, length)); + } else { + DPRINTFN(10, ("%s: req=%02x val=%02x " + "ind=%02x len=%02x [%8D]\n", + USBDEVNAME(sc->atu_dev), request, value, index, + length, data, " ")); + } + } +#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) { + DPRINTF(("%s: transfered 0x%x bytes" + " in\n", USBDEVNAME(sc->atu_dev), total_len)); + DPRINTF(("%s: dump [%10D]\n", + USBDEVNAME(sc->atu_dev), data, " ")); + } else { + if (total_len != length) + DPRINTF(("%s: ARG! 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 err; + u_int8_t statusreq[6]; + int idle_count = 0; + + DPRINTF(("%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) { + DPRINTF(("%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: AAARRGGG!!! FIX ME!\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; + } + atu_msleep(sc, 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 :) + */ + + request.AtCmd = CMD_SET_MIB; + request.AtReserved = 0; + 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]=(u_int32_t)data & 0x000000ff; + break; + case 2: + request.data[0]=(u_int32_t)data & 0x000000ff; + request.data[1]=(u_int32_t)data >> 8; + break; + default: + memcpy(request.data, data, size); + } + + err = atu_usb_request(sc, UT_WRITE_VENDOR_DEVICE, 0x0e, 0x0000, + 0x0000, request.AtSize+4, (u_int8_t *)&request); + if (err) + return err; + + DPRINTF(("%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); +} + +int +atu_start_ibss(struct atu_softc *sc) +{ + 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, sc->atu_ssid, sc->atu_ssidlen); + Request.SSIDSize = sc->atu_ssidlen; + Request.Channel = sc->atu_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; +} + +int +atu_start_scan(struct atu_softc *sc) +{ + struct atu_cmd_do_scan Scan; + usbd_status err; + int Cnt; + + Scan.Cmd = CMD_START_SCAN; + Scan.Reserved = 0; + 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, sc->atu_ssid, sc->atu_ssidlen); + Scan.SSID_Len = sc->atu_ssidlen; + + /* default values for scan */ + Scan.ScanType = ATU_SCAN_ACTIVE; + Scan.Channel = sc->atu_channel; + Scan.ProbeDelay = 3550; + Scan.MinChannelTime = 250; + Scan.MaxChannelTime = 3550; + + /* we like scans to be quick :) */ + /* the time we wait before sending probe's */ + Scan.ProbeDelay = 0; + /* the time we stay on one channel */ + Scan.MinChannelTime = 100; + Scan.MaxChannelTime = 200; + /* wether or not we scan all channels */ + Scan.InternationalScan = 0xc1; + +#ifdef ATU_DEBUG + if (atudebug) { + DPRINTF(("%s: scan cmd len=%02x\n", + USBDEVNAME(sc->atu_dev), sizeof(Scan))); + DPRINTF(("%s: scan cmd: %52D\n", + USBDEVNAME(sc->atu_dev), (u_int8_t *)&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 = {CMD_RADIO_ON, 0, 0}; + + if (sc->atu_radio == RadioIntersil) { + /* + * Intersil doesn't seem to need/support switching the radio + * on/off + */ + return 0; + } + + 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; + + DPRINTF(("%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) +{ + 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; + + DPRINTF(("%s: sending mac-addr\n", USBDEVNAME(sc->atu_dev))); + err = atu_send_mib(sc, MIB_MAC_ADDR__ADDR, sc->atu_mac_addr); + 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; + cmd.Size = sizeof(cmd) - 4; + + 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; + cmd.RTS_Threshold = 2347; + 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 (sc->atu_encrypt == ATU_WEP_OFF) + cmd.PrivacyInvoked = 0; + else + cmd.PrivacyInvoked = 1; + + cmd.ExcludeUnencrypted = 0; + cmd.EncryptionType = sc->atu_wepkeylen; + + /* Setting the SSID here doesn't seem to do anything */ + memset(cmd.SSID, 0, sizeof(cmd.SSID)); + memcpy(cmd.SSID, sc->atu_ssid, sc->atu_ssidlen); + cmd.SSID_Len = sc->atu_ssidlen; + + cmd.WEP_DefaultKeyID = sc->atu_wepkey; + memcpy(cmd.WEP_DefaultKey, sc->atu_wepkeys, + sizeof(cmd.WEP_DefaultKey)); + + cmd.ShortPreamble = 1; + cmd.ShortPreamble = 0; + 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, ®_domain); + if (err) { + DPRINTF(("%s: could not get regdomain!\n", + USBDEVNAME(sc->atu_dev))); + } + +#ifdef ATU_DEBUG + if (atudebug) { + DPRINTF(("%s: configlen=%02x\n", + USBDEVNAME(sc->atu_dev), sizeof(cmd))); + DPRINTF(("%s: configdata= %108D\n", + USBDEVNAME(sc->atu_dev), (u_int8_t *)&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; + + DPRINTF(("%s: completed initial config\n", + USBDEVNAME(sc->atu_dev))); + return 0; +} + +int +atu_join(struct atu_softc *sc) +{ + struct atu_cmd_join join; + u_int8_t status; + usbd_status err; + + join.Cmd = CMD_JOIN; + join.Reserved = 0x00; + join.Size = sizeof(join) - 4; + + DPRINTF(("%s: pre-join sc->atu_bssid=%s\n", + USBDEVNAME(sc->atu_dev), ether_sprintf(sc->atu_bssid))); + DPRINTF(("%s: mode=%d\n", USBDEVNAME(sc->atu_dev), + sc->atu_mode)); + memcpy(join.bssid, sc->atu_bssid, ETHER_ADDR_LEN); + memset(join.essid, 0x00, 32); + memcpy(join.essid, sc->atu_ssid, sc->atu_ssidlen); + join.essid_size = sc->atu_ssidlen; + join.bss_type = sc->atu_mode; + join.channel = sc->atu_channel; + + join.timeout = ATU_JOIN_TIMEOUT; + join.reserved = 0x00; + + DPRINTF(("%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 { + DPRINTF(("%s: joined BSS\n", USBDEVNAME(sc->atu_dev))); + } + return err; +} + +#if 0 +void +atu_airo_tap(struct atu_softc *sc, u_int8_t *pkt, u_int length, + struct at76c503_rx_buffer *at_hdr) +{ + struct mbuf *airo_pkt; + struct an_rxframe *airo_hdr; + struct wi_80211_hdr *wi_hdr; + struct wi_80211_hdr *wi_hdr_dst; + int wi_hdr_len; + u_int8_t *payload_ptr; + int s; + + MGETHDR(airo_pkt, M_DONTWAIT, MT_DATA); + if (airo_pkt == NULL) { + DPRINTF(("%s: airo_tap: could not get an mbuf!\n", + USBDEVNAME(sc->atu_dev))); + return; + } + + MCLGET(airo_pkt, M_DONTWAIT); + if (!(airo_pkt->m_flags & M_EXT)) { + DPRINTF(("%s: airo_tap: could not get an mbuf!\n", + USBDEVNAME(sc->atu_dev))); + m_freem(airo_pkt); + return; + } + + airo_pkt->m_len = airo_pkt->m_pkthdr.len = + MCLBYTES; + + airo_hdr = mtod(airo_pkt, struct an_rxframe *); + wi_hdr_dst = (struct wi_80211_hdr *)&airo_hdr->an_frame_ctl; + wi_hdr = (struct wi_80211_hdr *)pkt; + payload_ptr = mtod(airo_pkt, u_int8_t *) + sizeof(struct an_rxframe); + + bzero(airo_hdr, sizeof(struct an_rxframe)); + + if (at_hdr != NULL) { + /* It's a received packet : fill in receiver info */ + airo_hdr->an_rx_time = at_hdr->rx_time; + airo_hdr->an_rx_signal_strength = at_hdr->rssi; + airo_hdr->an_rx_rate = at_hdr->rx_rate; + + /* this is inaccurate when scanning! */ + airo_hdr->an_rx_chan = sc->atu_channel; + + } else { + /* It's a transmitted packet : make up as much as we can */ + airo_hdr->an_rx_time = 0; + airo_hdr->an_rx_signal_strength = 0xff; + airo_hdr->an_rx_rate = 4; + + airo_hdr->an_rx_chan = sc->atu_channel; + } + + if ((wi_hdr->frame_ctl & WI_FCTL_FTYPE) == WI_FTYPE_DATA) { + wi_hdr_len = sizeof(struct wi_80211_hdr); + } else { + wi_hdr_len = sizeof(struct wi_mgmt_hdr); + } + + airo_hdr->an_rx_payload_len = length - wi_hdr_len; + + bcopy(wi_hdr, wi_hdr_dst, wi_hdr_len); + airo_hdr->an_gaplen = 0; + + m_copyback(airo_pkt, sizeof(struct an_rxframe), length - wi_hdr_len, + pkt + wi_hdr_len); + + airo_pkt->m_pkthdr.rcvif = &sc->arpcom.ac_if; + s = splnet(); + IF_INPUT(&sc->arpcom.ac_if, airo_pkt); + splx(s); + + airo_pkt->m_pkthdr.len = airo_pkt->m_len = length - wi_hdr_len + + sizeof(struct an_rxframe); + + bpf_mtap(sc->atu_airobpf, airo_pkt); + m_free(airo_pkt); +} +#endif /* 0 */ + +int +atu_send_packet(struct atu_softc *sc, struct atu_chain *c) +{ + usbd_status err; + struct atu_txpkt *pkt; +#if 0 + struct ifnet *ifp = &sc->arpcom.ac_if; +#endif + /* Don't try to send when we're shutting down the driver */ + if (sc->atu_dying) + return(EIO); + +#if 0 + if (ifp->if_bpf) { + DPRINTF(("%s: bpf_tap on tx\n", USBDEVNAME(sc->atu_dev))); + bpf_tap(ifp->if_bpf, c->atu_buf, c->atu_length); + } + + /* drop the raw 802.11b packets to bpf */ + if (sc->atu_rawbpf) { + DPRINTF(("%s: raw bpf tap on TX :)\n", + USBDEVNAME(sc->atu_dev))); + bpf_tap(sc->atu_rawbpf, c->atu_buf, c->atu_length); + } +#endif + + pkt = (struct atu_txpkt *)c->atu_buf; + if (pkt->WiHeader.addr1[0]==0xff) { + printf("lal :)\n"); + + /* memcpy(pkt->WiHeader.addr1, sc->atu_bssid, 6); */ + /* + pkt->WiHeader.addr1[0]=0xe3; + pkt->WiHeader.addr1[1]=0x13; + pkt->WiHeader.addr1[2]=0x41; + pkt->WiHeader.addr1[3]=0x25; + pkt->WiHeader.addr1[4]=0x61; + pkt->WiHeader.addr1[5]=0xe3; + */ + + /* + pkt->WiHeader.addr1[0]=0x00; + pkt->WiHeader.addr1[1]=0x04; + pkt->WiHeader.addr1[2]=0xe2; + pkt->WiHeader.addr1[3]=0xa9; + pkt->WiHeader.addr1[4]=0xf5; + pkt->WiHeader.addr1[5]=0x28; + */ + + /* + pkt->WiHeader.addr1[0]=0x0; + pkt->WiHeader.addr1[1]=0x0; + pkt->WiHeader.addr1[2]=0x0; + pkt->WiHeader.addr1[3]=0x0; + pkt->WiHeader.addr1[4]=0x0; + pkt->WiHeader.addr1[5]=0x0; + */ + } + +#if 0 + /* drop the raw 802.11b packets to bpf with aironet header */ + if (sc->atu_airobpf) { + + DPRINTF(("%s: aironet bpf tap on TX :)\n", + USBDEVNAME(sc->atu_dev))); + + pkt = (struct atu_txpkt *)c->atu_buf; + + atu_airo_tap(sc, (u_int8_t *)&pkt->WiHeader, + c->atu_length - sizeof(struct at76c503_tx_buffer), + NULL); + } +#endif + +#ifdef ATU_NO_COPY_TX + 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); +#else /* ATU_NO_COPY_TX */ + usbd_setup_xfer(c->atu_xfer, sc->atu_ep[ATU_ENDPT_TX], + c, c->atu_buf, c->atu_length, 0, ATU_TX_TIMEOUT, atu_txeof); +#endif /* ATU_NO_COPY_TX */ + + /* Let's get this thing into the air! */ + c->atu_in_xfer = 1; + err = usbd_transfer(c->atu_xfer); + if (err != USBD_IN_PROGRESS) { + atu_stop(sc); + return(EIO); + } + + DPRINTF(("%s: tx packet...\n", USBDEVNAME(sc->atu_dev))); + return 0; +} + +int +atu_send_mgmt_packet(struct atu_softc *sc, struct atu_chain *c, + u_int16_t length) +{ + struct atu_mgmt_packet *packet; + + packet = (struct atu_mgmt_packet *)c->atu_buf; + + packet->athdr.wlength = length - sizeof(packet->athdr); + packet->athdr.tx_rate = 4; + packet->athdr.padding = 0; + memset(packet->athdr.reserved, 0x00, 4); + + /* DAAN Daan daan */ + packet->athdr.tx_rate = 0; + + packet->mgmt_hdr.duration = 0x8000; + memcpy(packet->mgmt_hdr.dst_addr, sc->atu_bssid, ETHER_ADDR_LEN); + memcpy(packet->mgmt_hdr.src_addr, sc->atu_mac_addr, ETHER_ADDR_LEN); + memcpy(packet->mgmt_hdr.bssid, sc->atu_bssid, ETHER_ADDR_LEN); + packet->mgmt_hdr.seq_ctl = 0; + + c->atu_length = length; + return atu_send_packet(sc, c); +} + +int +atu_authenticate(struct atu_softc *sc) +{ + usbd_status err; + struct atu_chain *ch; + struct atu_auth_packet *packet; + + /* + * now we should authenticate : + * 7.2.3.10 - page 64 of 802.11b spec + * 8.1 - page 74 of 802.11b spec + * see 7.3.1.9 - page 69 for status codes + * + * open systems : + * send: seq_nr=1 auth req + * recv: seq_nr=2 auth resp. (with status code) + * + * shared key systems : + * send: seq_nr=1 auth req + * recv: seq_nr=2 auth challenge (with status code & challenge + * text) + * send: seq_nr=3 auth reponse (wep encr challenge text) + * recv: seq_nr=4 auth result + * + * algorithm number : + * 0 = open + * 1 = shared + */ + + ch = SLIST_FIRST(&sc->atu_cdata.atu_mgmt_free); + if (ch == NULL) { + DPRINTF(("%s: authenticate: no mgmt transfers available\n", + USBDEVNAME(sc->atu_dev))); + return ENOMEM; + } + SLIST_REMOVE_HEAD(&sc->atu_cdata.atu_mgmt_free, atu_list); + + packet = (struct atu_auth_packet *)ch->atu_buf; + + packet->mgmt_hdr.frame_ctl = WI_FTYPE_MGMT | + IEEE80211_FC0_SUBTYPE_AUTH; + + packet->auth_hdr.wi_algo = 0; + packet->auth_hdr.wi_seq = 1; + packet->auth_hdr.wi_status = 0; + + DPRINTF(("%s: auth packet: %30D\n", USBDEVNAME(sc->atu_dev), + ((u_int8_t *)packet)+8, " ")); + + err = atu_send_mgmt_packet(sc, ch, sizeof(*packet)); + if (err) { + DPRINTF(("%s: could not send auth packet\n", + USBDEVNAME(sc->atu_dev))); + } + + /* + * TODO: implement shared key auth + */ + /* + packet->algoritm = 1; + packet->sequence = 3; + packet->status = 0; + + memcpy(packet->challenge, the_challenge_text, the_challenge_length); + + DPRINTF(("%s: auth packet: %30D\n", USBDEVNAME(sc->atu_dev), + ((u_int8_t *)packet)+8, " ")); + + if (sc->atu_encrypt & ATU_WEP_TX) { + packet->mgmt_hdr.frame_ctl |= WI_FCTL_WEP; + DPRINTF(("%s: ==> WEP on please\n", + USBDEVNAME(sc->atu_dev))); + } + + err = atu_send_mgmt_packet(sc, ch, sizeof(*packet) + challenge_len); + if (err) { + DPRINTF(("%s: could not send auth packet 2\n", + USBDEVNAME(sc->atu_dev))); + } + */ + return 0; +} + +int +atu_associate(struct atu_softc *sc) +{ + usbd_status err; + struct atu_chain *ch; + u_int8_t *ptr; + struct atu_assoc_packet *packet; + + /* + * associate : + * 7.2.3.4 - page 62 of 802.11b spec + * + */ + + ch = SLIST_FIRST(&sc->atu_cdata.atu_mgmt_free); + if (ch == NULL) { + DPRINTF(("%s: associate: no mgmt transfers left\n", + USBDEVNAME(sc->atu_dev))); + return ENOMEM; + } + SLIST_REMOVE_HEAD(&sc->atu_cdata.atu_mgmt_free, atu_list); + + packet = (struct atu_assoc_packet *)ch->atu_buf; + + packet->mgmt_hdr.frame_ctl = WI_FTYPE_MGMT | + IEEE80211_FC0_SUBTYPE_ASSOC_REQ; + + packet->capability = 1 + 32; /* ess & short preamble */ + packet->capability = 1; + packet->listen_interval = 100; /* beacon interval */ + + ptr = packet->data; + *ptr++ = WI_VAR_SSID; /* SSID */ + *ptr++ = sc->atu_ssidlen; + memcpy(ptr, sc->atu_ssid, sc->atu_ssidlen); + ptr += sc->atu_ssidlen; + + *ptr++ = WI_VAR_SRATES; /* supported rates */ + *ptr++ = 0x04; + *ptr++ = 0x82; + *ptr++ = 0x84; + *ptr++ = 0x8b; + *ptr++ = 0x96; + + DPRINTF(("%s: associate packet: %50D\n", + USBDEVNAME(sc->atu_dev), (u_int8_t *)packet, " ")); + + err = atu_send_mgmt_packet(sc, ch, sizeof(*packet) + 2 + + sc->atu_ssidlen + 6); + if (err) { + DPRINTF(("%s: could not send associate packet\n", + USBDEVNAME(sc->atu_dev))); + } + return 0; +} + +/* + * 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 + */ +int +atu_upload_internal_firmware(struct atu_softc *sc) +{ + int8_t state; + int bytes_left = 0; + u_int8_t *ptr = NULL; + int block_size; + int block = 0; + u_int8_t status[6]; + int err; + + /* + * 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 seperate '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 */ + switch (sc->atu_radio) { + case RadioRFMD: + ptr = atuwi_fw_rfmd_int; + bytes_left = sizeof(atuwi_fw_rfmd_int); + DPRINTF(("%s: loading RFMD firmware...\n", + USBDEVNAME(sc->atu_dev))); + break; + case RadioRFMD2958: + ptr = atuwi_fw_rfmd2958_int; + bytes_left = sizeof(atuwi_fw_rfmd2958_int); + DPRINTF(("%s: loading RFMD2958 firmware...\n", + USBDEVNAME(sc->atu_dev))); + break; + case RadioRFMD2958_SMC: + ptr = atuwi_fw_rfmd2958_smc_int; + bytes_left = sizeof(atuwi_fw_rfmd2958_smc_int); + DPRINTF(("%s: loading RFMD2958-smc firmware...\n", + USBDEVNAME(sc->atu_dev))); + break; + case RadioIntersil: + ptr = atuwi_fw_intersil_int; + bytes_left = sizeof(atuwi_fw_intersil_int); + DPRINTF(("%s: loading Intersil firmware...\n", + USBDEVNAME(sc->atu_dev))); + break; + default: + DPRINTF(("%s: unknown device type?\n", USBDEVNAME(sc->atu_dev))); + bytes_left = 0; + break; + } + + state = atu_get_dfu_state(sc); + + while (bytes_left >= 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 err; + } + /* 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; + DPRINTF(("%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 err; + } + + ptr += block_size; + bytes_left -= block_size; + if (block_size == 0) + bytes_left = -1; + break; + + default: + atu_msleep(sc, 100); + DPRINTF(("%s: sleeping for a while\n", + USBDEVNAME(sc->atu_dev))); + } + + 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 err; + } + + DPRINTF(("%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 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... + */ + atu_msleep(sc, 56+100); + + /* reset the device to get the firmware to boot */ + DPRINTF(("%s: trying to reset device...\n", + USBDEVNAME(sc->atu_dev))); + err = atu_reset(sc); + if (err) { + DPRINTF(("%s: reset failed...\n", USBDEVNAME(sc->atu_dev))); + return err; + } + + DPRINTF(("%s: internal firmware upload done\n", + USBDEVNAME(sc->atu_dev))); + return 0; +} + +int +atu_upload_external_firmware(struct atu_softc *sc) +{ + u_int8_t *ptr = NULL; + int bytes_left = 0; + int block_size; + int block = 0; + u_int8_t mode; + u_int8_t channel; + int err; + + err = atu_get_opmode(sc, &mode); + if (err) { + DPRINTF(("%s: could not get opmode\n", USBDEVNAME(sc->atu_dev))); + return err; + } + DPRINTF(("%s: opmode: %d\n", USBDEVNAME(sc->atu_dev), mode)); + + if (mode == MODE_NETCARD) { + DPRINTF(("%s: device doesn't need external " + "firmware\n", USBDEVNAME(sc->atu_dev))); + return 0; + } + if (mode != MODE_NOFLASHNETCARD) { + DPRINTF(("%s: EEK! 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 firmware must have been + * already uploaded... + */ + err = atu_get_mib(sc, MIB_PHY__CHANNEL, &channel); + if (! err) { + DPRINTF(("%s: external firmware has already been " + "downloaded\n", USBDEVNAME(sc->atu_dev))); + return 0; + } + + switch (sc->atu_radio) { + case RadioRFMD: + ptr = atuwi_fw_rfmd_ext; + bytes_left = sizeof(atuwi_fw_rfmd_ext); + DPRINTF(("%s: loading external RFMD firmware\n", + USBDEVNAME(sc->atu_dev))); + break; + case RadioRFMD2958: + ptr = atuwi_fw_rfmd2958_ext; + bytes_left = sizeof(atuwi_fw_rfmd2958_ext); + DPRINTF(("%s: loading external RFMD2958 " + "firmware\n", USBDEVNAME(sc->atu_dev))); + break; + case RadioRFMD2958_SMC: + ptr = atuwi_fw_rfmd2958_smc_ext; + bytes_left = sizeof(atuwi_fw_rfmd2958_smc_ext); + DPRINTF(("%s: loading external RFMD2958-smc " + "firmware\n", USBDEVNAME(sc->atu_dev))); + break; + case RadioIntersil: + ptr = atuwi_fw_intersil_ext; + bytes_left = sizeof(atuwi_fw_intersil_ext); + DPRINTF(("%s: loading external Intersil " + "firmware\n", USBDEVNAME(sc->atu_dev))); + break; + default: + DPRINTF(("%s: unknown device type?\n", USBDEVNAME(sc->atu_dev))); + bytes_left = 0; + break; + } + + while (bytes_left) { + if (bytes_left > 1024) + block_size = 1024; + else + block_size = bytes_left; + + DPRINTF(("%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 err; + } + + 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 err; + } + + /* + * 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) { + atu_msleep(sc, 21 + 100); + } + + DPRINTF(("%s: external firmware upload done\n", + USBDEVNAME(sc->atu_dev))); + return 0; +} + +int +atu_get_card_config(struct atu_softc *sc) +{ + 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(sc->atu_mac_addr, rfmd_conf.MACAddr, ETHER_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(sc->atu_mac_addr, intersil_conf.MACAddr, + ETHER_ADDR_LEN); + break; + } + return 0; +} + +/* + * Probe for an AT76c503 chip. + */ +USB_MATCH(atu) +{ + USB_MATCH_START(atu, uaa); + struct atu_type *t; + + if (!uaa->iface) + return(UMATCH_NONE); + + t = atu_devs; + while(t->atu_vid) { + if (uaa->vendor == t->atu_vid && + uaa->product == t->atu_pid) { + return(UMATCH_VENDOR_PRODUCT); + } + t++; + } + return(UMATCH_NONE); +} + +/* + * this routine gets called from the mgmt thread at least once a second. + * if we return 0 the thread will go to sleep, if we return 1 we will be + * called again immediately (like 'continue' does in a while-loop) + */ +int +atu_mgmt_state_machine(struct atu_softc *sc) +{ + struct atu_mgmt *vars = &sc->atu_mgmt_vars; + usbd_status err; + u_int8_t statusreq[6]; + int s; + + if (sc->atu_mgmt_flags & ATU_CHANGED_SETTINGS) { + sc->atu_mgmt_flags &= ~ATU_CHANGED_SETTINGS; + sc->atu_mgmt_flags &= ~ATU_FOUND_BSSID; + sc->atu_mgmt_flags &= ~ATU_AUTH_OK; + sc->atu_mgmt_flags &= ~ATU_RE_AUTH; + sc->atu_mgmt_flags &= ~ATU_ASSOC_OK; + sc->atu_mgmt_flags |= ATU_SEARCHING; + vars->state = STATE_LISTENING; + vars->retry = 0; + } + + DPRINTF(("%s: [state=%s, retry=%d, chan=%d " + "enc=%d]\n", USBDEVNAME(sc->atu_dev), atu_mgmt_statename[vars->state], + vars->retry, sc->atu_channel, sc->atu_encrypt)); + + /* Fall back to authentication if needed */ + /* TODO: should we only allow this when in infra-mode? */ + if ((sc->atu_mgmt_flags & ATU_RE_AUTH) && + (vars->state >= STATE_AUTHENTICATING)) { + vars->state = STATE_AUTHENTICATING; + sc->atu_mgmt_flags &= ~(ATU_RE_AUTH | ATU_AUTH_OK | + ATU_ASSOC_OK); + vars->retry = 0; + } + + /* Fall back to association if needed */ + /* TODO: should we only allow this when in infra-mode? */ + if ((sc->atu_mgmt_flags & ATU_RE_ASSOC) && + (vars->state >= STATE_ASSOCIATING)) { + vars->state = STATE_ASSOCIATING; + sc->atu_mgmt_flags &= ~(ATU_RE_ASSOC | ATU_AUTH_OK | + ATU_ASSOC_OK); + vars->retry = 0; + } + + switch (vars->state) { + case STATE_NONE: + /* awaiting orders */ + break; + case STATE_LISTENING: + /* do some nifty scanning here */ + + if (sc->atu_mgmt_flags & ATU_FOUND_BSSID) { + vars->state = STATE_JOINING; + vars->retry = 0; + return 1; + } + s = splusb(); + err = atu_get_cmd_status(sc, CMD_JOIN, statusreq); + if (err) { + DPRINTF(("%s: get_cmd_status failed in mgmt_loop\n", + USBDEVNAME(sc->atu_dev))); + vars->state = STATE_GIVEN_UP; + vars->retry = 0; + } + if (statusreq[5]==STATUS_IN_PROGRESS) { + DPRINTF(("%s: scanning in progress...\n", + USBDEVNAME(sc->atu_dev))); + } else { + err = atu_start_scan(sc); + if (vars->retry++ > ATU_SCAN_RETRIES && + sc->atu_mode == AD_HOC_MODE) { + DPRINTF(("%s: scanned long enough\n", + USBDEVNAME(sc->atu_dev))); + sc->atu_mgmt_flags &= ~ATU_SEARCHING; + vars->state = STATE_CREATING_IBSS; + vars->retry = 0; + } + if (err) { + DPRINTF(("%s: get_cmd_failed in mgmt_loop\n", + USBDEVNAME(sc->atu_dev))); + vars->state = STATE_GIVEN_UP; + vars->retry = 0; + } + } + splx(s); + break; + case STATE_JOINING: + DPRINTF(("%s: going to join\n", + USBDEVNAME(sc->atu_dev))); + err = atu_join(sc); + if (err) { + if (vars->retry++ > ATU_JOIN_RETRIES) { + if (sc->atu_mode == AD_HOC_MODE) + vars->state = STATE_CREATING_IBSS; + else + vars->state = STATE_GIVEN_UP; + vars->retry = 0; + } + DPRINTF(("%s: error joining\n", + USBDEVNAME(sc->atu_dev))); + } else { + if (sc->atu_mode == AD_HOC_MODE) + vars->state = STATE_HAPPY_NETWORKING; + else + vars->state = STATE_AUTHENTICATING; + vars->retry = 0; + } + break; + case STATE_AUTHENTICATING: + if (sc->atu_mgmt_flags & ATU_AUTH_OK) { + vars->state = STATE_ASSOCIATING; + vars->retry = 0; + return 1; + } + + DPRINTF(("%s: trying authentication\n", + USBDEVNAME(sc->atu_dev))); + atu_authenticate(sc); + if (vars->retry++ > ATU_AUTH_RETRIES) { + vars->state = STATE_GIVEN_UP; + DPRINTF(("%s: error authenticating...\n", + USBDEVNAME(sc->atu_dev))); + } + break; + case STATE_ASSOCIATING: + if (sc->atu_mgmt_flags & ATU_ASSOC_OK) { + vars->state = STATE_HAPPY_NETWORKING; + vars->retry = 0; + return 1; + } + DPRINTF(("%s: trying to associate\n", + USBDEVNAME(sc->atu_dev))); + atu_associate(sc); + if (vars->retry++ > ATU_ASSOC_RETRIES) { + vars->state = STATE_GIVEN_UP; + DPRINTF(("%s: error associating...\n", + USBDEVNAME(sc->atu_dev))); + } + break; + case STATE_CREATING_IBSS: + DPRINTF(("%s: trying to create IBSS\n", + USBDEVNAME(sc->atu_dev))); + err = atu_start_ibss(sc); + if (err) { + if (vars->retry++ > ATU_IBSS_RETRIES) + vars->state = STATE_GIVEN_UP; + DPRINTF(("atu:%d: error creating IBSS...\n", + USBDEVNAME(sc->atu_dev))); + } else { + vars->state = STATE_HAPPY_NETWORKING; + vars->retry = 0; + } + break; + case STATE_HAPPY_NETWORKING: + /* happy networking + * + * TODO: + * we should bounce back to previous states from here + * on beacon timeout, disassociate or deauthenticate + * + * (but none of that has been implemented at this time) + */ + break; + case STATE_GIVEN_UP: + /* + * can only leave this state if someone changes the + * config + */ + break; + } + + if (vars->state == STATE_HAPPY_NETWORKING) + sc->atu_mgmt_flags |= ATU_NETWORK_OK; + else + sc->atu_mgmt_flags &= ~ATU_NETWORK_OK; + return 0; +} + +void +atu_mgmt_loop(void *arg) +{ + struct atu_softc *sc = arg; + int again; + int s; + + DPRINTF(("%s: mgmt task initialised\n", USBDEVNAME(sc->atu_dev))); + + sc->atu_mgmt_vars.state = STATE_NONE; + sc->atu_mgmt_vars.retry = 0; + + while (!sc->atu_dying) { + s = splnet(); + again = atu_mgmt_state_machine(sc); + while (again) + again = atu_mgmt_state_machine(sc); + splx(s); + + /* + * wait for something to happen (but not too long :) + * if someone changes the config or a mgmt packet is received + * we will be waken up + */ + tsleep(sc, PZERO | PCATCH, "atum", ATU_MGMT_INTERVAL); + } + + DPRINTF(("%s: mgmt thread stops now...\n", + USBDEVNAME(sc->atu_dev))); + sc->atu_dying++; + sc->atu_mgmt_flags &= ~ATU_TASK_RUNNING; + kthread_exit(0); +} + +int +atu_media_change(struct ifnet *ifp) +{ + struct atu_softc *sc; + struct ifmedia_entry *ime; + + sc = ifp->if_softc; + ime = sc->atu_media.ifm_cur; + + /* TODO: fully implement - see if_wi.c @ 1189 */ + + DPRINTF(("%s: subtype=%d %d\n", USBDEVNAME(sc->atu_dev), + IFM_SUBTYPE(ime->ifm_media), ime->ifm_media)); + + if ((ime->ifm_media & IFM_IEEE80211_ADHOC) && + (sc->atu_mode != AD_HOC_MODE)) { + DPRINTF(("%s: mode changed to adhoc\n", + USBDEVNAME(sc->atu_dev))); + sc->atu_mode = AD_HOC_MODE; + sc->atu_mgmt_flags |= ATU_CHANGED_SETTINGS; + wakeup(sc); + } + + if ((!(ime->ifm_media & IFM_IEEE80211_ADHOC)) && + (sc->atu_mode != INFRASTRUCTURE_MODE)) { + DPRINTF(("%s: mode changed to infra\n", + USBDEVNAME(sc->atu_dev))); + sc->atu_mode = INFRASTRUCTURE_MODE; + sc->atu_mgmt_flags |= ATU_CHANGED_SETTINGS; + wakeup(sc); + } + + DPRINTF(("%s: media_change...\n", USBDEVNAME(sc->atu_dev))); + return 0; +} + +void +atu_media_status(struct ifnet *ifp, struct ifmediareq *req) +{ + struct atu_softc *sc; + + sc = ifp->if_softc; + + /* TODO: fully implement */ + + req->ifm_status = IFM_AVALID; + req->ifm_active = IFM_IEEE80211; + + if (sc->atu_mgmt_flags & ATU_NETWORK_OK) + req->ifm_status |= IFM_ACTIVE; + + /* req->ifm_active |= ieee80211_rate2media(2*11, IEEE80211_T_DS); */ + + if (sc->atu_mode == AD_HOC_MODE) { + req->ifm_active |= IFM_IEEE80211_ADHOC; + } + DPRINTF(("%s: atu_media_status\n", USBDEVNAME(sc->atu_dev))); +} + +/* + * Attach the interface. Allocate softc structures, do + * setup and ethernet/BPF attach. + */ +USB_ATTACH(atu) +{ + USB_ATTACH_START(atu, sc, uaa); + char devinfo[1024]; + struct ifnet *ifp; + usbd_status err; + usbd_device_handle dev = uaa->device; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + int i, s; + u_int8_t mode; + struct atu_type *t; + struct atu_fw fw; + + usbd_devinfo(uaa->device, 0, devinfo, sizeof devinfo); + USB_ATTACH_SETUP; + printf("%s: %s\n", USBDEVNAME(sc->atu_dev), devinfo); + + 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 = self->dv_unit; + sc->atu_udev = dev; + + id = usbd_get_interface_descriptor(sc->atu_iface); + + /* + * look up the radio_type for the device + * basically does the same as USB_MATCH + */ + t = atu_devs; + while(t->atu_vid) { + if (uaa->vendor == t->atu_vid && + uaa->product == t->atu_pid) { + sc->atu_radio = t->atu_radio; + sc->atu_quirk = t->atu_quirk; + } + t++; + } + + s = splnet(); + + /* + * 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); + if (err || (mode != MODE_NETCARD && + mode != MODE_NOFLASHNETCARD)) { + DPRINTF(("%s: starting internal firmware download\n", + USBDEVNAME(sc->atu_dev))); + + /* upload internal firmware */ + err = atu_upload_internal_firmware(sc); + if (err) { + splx(s); + USB_ATTACH_ERROR_RETURN; + } + + DPRINTF(("%s: done...\n", + USBDEVNAME(sc->atu_dev))); + splx(s); + printf("trying reset now\n"); + USB_ATTACH_NEED_RESET; + } + + uaa->iface = sc->atu_iface; + + /* upload external firmware */ + DPRINTF(("%s: starting external firmware download\n", + USBDEVNAME(sc->atu_dev))); + err = atu_upload_external_firmware(sc); + if (err) { + splx(s); + USB_ATTACH_ERROR_RETURN; + } + + /* Find endpoints. */ + for (i = 0; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(uaa->iface, i); + if (!ed) { + DPRINTF(("%s: num_endp:%d\n", USBDEVNAME(sc->atu_dev), + uaa->iface->idesc->bNumEndpoints)); + DPRINTF(("%s: couldn't get ep %d\n", + USBDEVNAME(sc->atu_dev), i)); + splx(s); + USB_ATTACH_ERROR_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; + } + } + + s = splnet(); + + /* read device config & get MAC address */ + err = atu_get_card_config(sc); + if (err) { + DPRINTF(("%s: could not get card cfg!\n", USBDEVNAME(sc->atu_dev))); + splx(s); + USB_ATTACH_ERROR_RETURN; + } + + /* DEBUG : try to get firmware version */ + err = atu_get_mib(sc, MIB_FW_VERSION, sizeof(fw), 0, + (u_int8_t *)&fw); + if (!err) { +#if 0 + DPRINTF(("%s: firmware: maj:%d min:%d patch:%d build:%d\n", + USBDEVNAME(sc->atu_dev), fw.major, fw.minor, + fw.patch, fw.build)); +#endif + } else { + DPRINTF(("%s: get firmware version failed\n", + USBDEVNAME(sc->atu_dev))); + } + + /* Show the world our MAC address */ + DPRINTF(("%s: address %s\n", USBDEVNAME(sc->atu_dev), + ether_sprintf(sc->atu_mac_addr))); + + bcopy(sc->atu_mac_addr, + (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + for (i=0; i<ATU_AVG_TIME; i++) + sc->atu_signalarr[i] = 0; + sc->atu_signaltotal = 0; + sc->atu_signalptr = 0; + sc->atu_cdata.atu_tx_inuse = 0; + sc->atu_encrypt = ATU_WEP_OFF; + sc->atu_wepkeylen = ATU_WEP_104BITS; + sc->atu_wepkey = 0; + sc->atu_mgmt_flags = 0; + + bzero(sc->atu_bssid, ETHER_ADDR_LEN); + sc->atu_ssidlen = strlen(ATU_DEFAULT_SSID); + memcpy(sc->atu_ssid, ATU_DEFAULT_SSID, sc->atu_ssidlen); + sc->atu_channel = 10; + sc->atu_mode = INFRASTRUCTURE_MODE; + sc->atu_encrypt = ATU_WEP_OFF; + + /* Initialise transfer lists */ + SLIST_INIT(&sc->atu_cdata.atu_rx_free); + SLIST_INIT(&sc->atu_cdata.atu_tx_free); + SLIST_INIT(&sc->atu_cdata.atu_mgmt_free); + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + strncpy(ifp->if_xname, USBDEVNAME(sc->atu_dev), IFNAMSIZ); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = atu_ioctl; + ifp->if_start = atu_start; + ifp->if_watchdog = atu_watchdog; + ifp->if_baudrate = 10000000; + ifp->if_mtu = ATU_DEFAULT_MTU; + + IFQ_SET_READY(&ifp->if_snd); + + /* + * Call MI attach routine. + */ + if_attach(ifp); + Ether_ifattach(ifp, sc->atu_mac_addr); + + sc->atu_dying = 0; + + /* setup ifmedia interface */ + ifmedia_init(&sc->atu_media, 0, atu_media_change, + atu_media_status); + +#define ADD(s, o) ifmedia_add(&sc->atu_media, \ + IFM_MAKEWORD(IFM_IEEE80211, (s), (o), 0), 0, NULL) + + ADD(IFM_AUTO, 0); + ADD(IFM_AUTO, IFM_IEEE80211_ADHOC); + + /* + * TODO: + * add a list of supported rates here. + * (can't do that as long as we only support 'auto fallback' + * + for (i = 0; i < nrate; i++) { + r = ic->ic_sup_rates[i]; + mword = ieee80211_rate2media(r, IEEE80211_T_DS); + if (mword == 0) + continue; + printf("%s%d%sMbps", (i != 0 ? " " : ""), + (r & IEEE80211_RATE_VAL) / 2, ((r & 0x1) != 0 ? ".5" : "")); + ADD(mword, 0); + if (ic->ic_flags & IEEE80211_F_HASHOSTAP) + ADD(mword, IFM_IEEE80211_HOSTAP); + if (ic->ic_flags & IEEE80211_F_HASIBSS) + ADD(mword, IFM_IEEE80211_ADHOC); + ADD(mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); + } + printf("\n"); + */ + + ADD(11, 0); + ADD(11, IFM_IEEE80211_ADHOC); + + ifmedia_set(&sc->atu_media, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, + 0)); +#undef ADD + + /* attach to bpf for raw 802.11 packets */ +#if 0 +#if NBPFILTER > 0 + bpfattach(&sc->atu_rawbpf, ifp, DLT_IEEE802_11_RADIO, + sizeof(struct ieee80211_frame) + 64); +#endif +#endif + + splx(s); + USB_ATTACH_SUCCESS_RETURN; +} + +USB_DETACH(atu) +{ + USB_DETACH_START(atu, sc); + struct ifnet *ifp = &sc->arpcom.ac_if; + + atu_stop(sc); + +#if 0 +#if NBPFILTER > 0 + bpfdetach(ipf); +#endif +#endif + + ieee80211_ifdetach(ifp); + if_detach(ifp); + + if (sc->atu_ep[ATU_ENDPT_TX] != NULL) + usbd_abort_pipe(sc->atu_ep[ATU_ENDPT_TX]); + if (sc->atu_ep[ATU_ENDPT_RX] != NULL) + usbd_abort_pipe(sc->atu_ep[ATU_ENDPT_RX]); + 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_deactivate(&sc->atu_ec.ec_if); + sc->atu_dying = 1; + 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 -- packet dropped!\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 -- packet dropped!\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_xfer_list_init(struct atu_softc *sc, struct atu_chain *ch, + int listlen, int need_mbuf, int bufsize, struct atu_list_head *list) +{ + struct atu_cdata *cd; + int i; + + cd = &sc->atu_cdata; + + DPRINTF(("%s: list init (%d entries of %d bytes)\n", + USBDEVNAME(sc->atu_dev), listlen, bufsize)); + + for (i = 0; i < listlen; i++) { + ch->atu_sc = sc; + ch->atu_idx = i; + if (ch->atu_xfer == NULL) { + ch->atu_xfer = usbd_alloc_xfer(sc->atu_udev); + if (ch->atu_xfer == NULL) + return(ENOBUFS); + } + + if (need_mbuf) { + if (atu_newbuf(sc, ch, NULL) == ENOBUFS) + return(ENOBUFS); + } else { + ch->atu_mbuf = NULL; + } + + if ((bufsize > 0) && (ch->atu_buf == NULL)) { +#ifdef ATU_NO_COPY_TX + ch->atu_buf = usbd_alloc_buffer(ch->atu_xfer, + bufsize); + if (ch->atu_buf == NULL) + return(ENOBUFS); +#else /* ATU_NO_COPY_TX */ + ch->atu_buf = malloc(bufsize, M_USBDEV, + M_NOWAIT); + if (ch->atu_buf == NULL) + return(ENOBUFS); +#endif /* ATU_NO_COPY_TX */ + } + + if (list != NULL) { + SLIST_INSERT_HEAD(list, ch, atu_list); + } + + ch++; + } + 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) { +#ifdef ATU_NO_COPY_TX + /* + * usbdi.c cleans up for us + */ +#else /* ATU_NO_COPY_TX */ + free(ch[i].atu_buf, M_USBDEV); +#endif /* ATU_NO_COPY_TX */ + 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; + } + } +} + +void +atu_print_beacon(struct atu_softc *sc, struct atu_rxpkt *pkt) +{ + u_int8_t *ptr; + struct tlv *tlv; + u_int8_t *end; + u_int8_t tmp; + u_int8_t rate; + + /* Let's have a closer look at this beacon... */ + ptr = (u_int8_t *)pkt->WiHeader.addr4 + 12; + end = ptr + pkt->AtHeader.wlength - 24 - 12 - 4; + tlv = (struct tlv *)ptr; + + while ((ptr<end) && (ptr + 2 + tlv->length <= end)) { + switch (tlv->type) { + case WI_VAR_SSID: /* SSID */ + /* sanity check */ + if (tlv->length > 32) + break; + + tmp = tlv->value[tlv->length]; + tlv->value[tlv->length] = 0; + DPRINTF(("%s: ssid=[%s]\n", + USBDEVNAME(sc->atu_dev), tlv->value)); + tlv->value[tlv->length] = tmp; + break; + case WI_VAR_SRATES: /* Supported rates */ + for (rate=0; rate<tlv->length; rate++) { + tmp = tlv->value[rate] & (~0x80); + DPRINTF(("%s: rate: %d kbps " + "(%02x)\n", USBDEVNAME(sc->atu_dev), 500 * tmp, + tlv->value[rate])); + } + break; + case WI_VAR_DS: /* DS (channel) */ + DPRINTF(("%s: channel=%d\n", + USBDEVNAME(sc->atu_dev), *tlv->value)); + break; + default : + DPRINTF(("%s: tlv: t=%02x l=%02x " + "v[0]=%02x\n", USBDEVNAME(sc->atu_dev), tlv->type, + tlv->length, tlv->value[0])); + } + + ptr += 2 + tlv->length; + tlv = (struct tlv *)ptr; + } +} + +void +atu_handle_mgmt_packet(struct atu_softc *sc, struct atu_rxpkt *pkt) +{ + u_int8_t *ptr; + struct tlv *tlv; + u_int8_t *end; + u_int8_t tmp; + int match; + int match_channel = 1; + struct wi_80211_beacon *beacon; + struct wi_mgmt_auth_hdr *auth; + struct wi_mgmt_deauth_hdr *deauth; + struct wi_mgmt_disas_hdr *deassoc; + struct wi_mgmt_asresp_hdr *assoc; + + switch (pkt->WiHeader.frame_ctl & WI_FCTL_STYPE) { + case WI_STYPE_MGMT_AUTH: + DPRINTF(("%s: received auth response...\n", + USBDEVNAME(sc->atu_dev))); + + auth = (struct wi_mgmt_auth_hdr *)pkt->WiHeader.addr4; + + if (auth->wi_seq != 2) { + DPRINTF(("%s: auth wrong seq.nr (%x)\n", + USBDEVNAME(sc->atu_dev), auth->wi_seq)); + break; + } + if (auth->wi_status != 0) { + DPRINTF(("%s: auth status error (%x)\n", + USBDEVNAME(sc->atu_dev), auth->wi_status)); + break; + } + + /* TODO: should check bssid & mac ! */ + + sc->atu_mgmt_flags |= ATU_AUTH_OK; + wakeup(sc); + + /* + * TODO: DAAN Daan daan - Add challenge text blah blah + * (for shared-key systems) + */ + /* + memcpy(the_challenge_text, ((u_int8_t *)&pkt->WiHeader) + 30, + the_challenge_length); + DPRINTF(("%s: challenge= %100D\n", + USBDEVNAME(sc->atu_dev), the_challende_text, " ")); + */ + break; + + case WI_STYPE_MGMT_DEAUTH: + DPRINTF(("%s: the AP has de-authenticated " + "us\n", USBDEVNAME(sc->atu_dev))); + + deauth = (struct wi_mgmt_deauth_hdr *)pkt->WiHeader.addr4; + + DPRINTF(("autiw%d: de-authentication reason: %04x\n", + USBDEVNAME(sc->atu_dev), deauth->wi_reason)); + + /* TODO: should check bssid & mac ! */ + + /* wake up the state machine to get us re-authenticated */ + sc->atu_mgmt_flags |= ATU_RE_AUTH; + wakeup(sc); + break; + + case WI_STYPE_MGMT_ASRESP: + DPRINTF(("%s: received assoc response...\n", + USBDEVNAME(sc->atu_dev))); + + assoc = (struct wi_mgmt_asresp_hdr *)pkt->WiHeader.addr4; + + if (assoc->wi_status == 0) { + sc->atu_mgmt_flags |= ATU_ASSOC_OK; + wakeup(sc); + } else { + DPRINTF(("%s: assoc status error (%x)\n", + USBDEVNAME(sc->atu_dev), assoc->wi_status)); + break; + } + + /* TODO: should check bssid & mac ! */ + wakeup(sc); + + break; + + case WI_STYPE_MGMT_DISAS: + DPRINTF(("%s: the AP has de-associated us", + USBDEVNAME(sc->atu_dev))); + + deassoc = (struct wi_mgmt_disas_hdr *)pkt->WiHeader.addr4; + + DPRINTF(("autiw%d: de-association reason: %04x\n", + USBDEVNAME(sc->atu_dev), deassoc->wi_reason)); + + /* TODO: should check bssid & mac ! */ + + /* wake up the state machine to get us re-authenticated */ + sc->atu_mgmt_flags |= ATU_RE_ASSOC; + wakeup(sc); + break; + + case WI_STYPE_MGMT_PROBERESP: + DPRINTF(("%s: PROBE RESPONSE\n", + USBDEVNAME(sc->atu_dev))); + /* FALLTHROUGH */ + case WI_STYPE_MGMT_BEACON: + + beacon = (struct wi_80211_beacon *)&pkt->WiHeader.addr4; + + /* Show beacon src MAC & signal strength */ + DPRINTF(("%s: mgmt bssid=%s", + USBDEVNAME(sc->atu_dev), ether_sprintf(pkt->WiHeader.addr3))); + DPRINTF((" mac=%s signal=%d\n", + ether_sprintf(pkt->WiHeader.addr2), pkt->AtHeader.rssi)); + + if (atudebug & FLAG_SIGNAL) { + /* + * calculate average signal strength, can be very + * usefull when precisely aiming antenna's + * NOTE: this is done on ALL beacons, so multiple + * stations can end up in the average. this only + * works well if we're only receiving one station. + */ + sc->atu_signaltotal += pkt->AtHeader.rssi; + sc->atu_signaltotal -= + sc->atu_signalarr[sc->atu_signalptr]; + sc->atu_signalarr[sc->atu_signalptr] = + pkt->AtHeader.rssi; + sc->atu_signalptr=(sc->atu_signalptr+1) % + ATU_AVG_TIME; + DPRINTF(("%s: mgmt mac=%s signal=%02d" + " ptr=%02d avg=%02d.%02d\n", USBDEVNAME(sc->atu_dev), + ether_sprintf(pkt->WiHeader.addr2), pkt->AtHeader.rssi, + sc->atu_signalptr, + sc->atu_signaltotal / ATU_AVG_TIME, + (sc->atu_signaltotal * 100 / ATU_AVG_TIME) % + 100)); + } + + DPRINTF(("%s: mgmt capabilities=%04x " + "(mode=%s, wep=%s, short-preamble=%s)\n", USBDEVNAME(sc->atu_dev), + beacon->flags, + (beacon->flags & IEEE80211_CAPINFO_ESS) ? + "infra" : "ad-hoc", + (beacon->flags & IEEE80211_CAPINFO_PRIVACY) ? + "on" : "off", + (beacon->flags & IEEE80211_CAPINFO_SHORT_PREAMBLE) ? + "yes" : "no")); + + if (atudebug & FLAG_BEACONSFULL) + atu_print_beacon(sc, pkt); + + if (!(sc->atu_mgmt_flags & ATU_SEARCHING)) + break; + + /* Let's have a closer look at this beacon... */ + ptr = (u_int8_t *)pkt->WiHeader.addr4 + 12; + end = ptr + pkt->AtHeader.wlength - 24 - 12 - 4; + tlv = (struct tlv *)ptr; + match = 0; + while ((ptr<end) && (ptr + 2 + tlv->length <= end)) { + switch (tlv->type) { + case WI_VAR_SSID: /* SSID */ + /* sanity check */ + if (tlv->length > 32) + break; + + tmp = tlv->value[tlv->length]; + tlv->value[tlv->length] = 0; + sc->atu_ssid[sc->atu_ssidlen] = 0; + if (!strcmp(tlv->value, sc->atu_ssid)) { + match = 1; + } + tlv->value[tlv->length] = tmp; + break; + case WI_VAR_SRATES: /* Supported rates */ + /* + * TODO: should check if we support all + * mandatory rates + */ + break; + case WI_VAR_DS: /* DS (channel) */ + if (match) + match_channel = *tlv->value; + break; + } + + ptr += 2 + tlv->length; + tlv = (struct tlv *)ptr; + } + + /* check mode... */ + beacon = (struct wi_80211_beacon *)&pkt->WiHeader.addr4; + if (match) { + if ((sc->atu_mode == AD_HOC_MODE) && + (beacon->flags & IEEE80211_CAPINFO_ESS)) + match = 0; + if ((sc->atu_mode == INFRASTRUCTURE_MODE) && + (!(beacon->flags & IEEE80211_CAPINFO_ESS))) + match = 0; + + if (!match) { + DPRINTF(("%s: SSID matches, " + "but we're in %s mode instead of %s\n", + USBDEVNAME(sc->atu_dev), + (sc->atu_mode == AD_HOC_MODE) ? + "adhoc" : "infra", + (sc->atu_mode == AD_HOC_MODE) ? + "infra" : "adhoc")); + break; + } + } + + if (match) { + DPRINTF(("%s: ==> MATCH! (BSSID=%s, ch=%d)\n", + USBDEVNAME(sc->atu_dev), + ether_sprintf(pkt->WiHeader.addr3), match_channel)); + + /* + * TODO: should do some channel-checking here instead + * of just ignoring the channel the user sets + */ + + memcpy(sc->atu_bssid, pkt->WiHeader.addr3, + ETHER_ADDR_LEN); + sc->atu_channel = match_channel; + + sc->atu_mgmt_flags &= ~ATU_SEARCHING; + sc->atu_mgmt_flags |= ATU_FOUND_BSSID; + } + + break; + + default: + DPRINTF(("%s: FIXME: unhandled mgmt type! " + "(stype=%x)\n", USBDEVNAME(sc->atu_dev), + pkt->WiHeader.frame_ctl & WI_FCTL_STYPE)); + } +} + +/* + * 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 ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + u_int32_t total_len; + int s; + + struct atu_rxpkt *pkt; + struct ether_header *eth_hdr; + int offset; + + DPRINTFN(10, ("%s: atu_rxeof: enter\n", USBDEVNAME(sc->atu_dev))); + + if (sc->atu_dying) + return; + + if (!(ifp->if_flags & IFF_RUNNING)) + return; + + DPRINTF(("%s: got a packet\n", USBDEVNAME(sc->atu_dev))); + + 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( + sc->atu_ep[ATU_ENDPT_RX]); + goto done; + } + + usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); + + m = c->atu_mbuf; + + if (total_len <= 1) { + DPRINTF(("%s: atuw_rxeof: too short\n", + USBDEVNAME(sc->atu_dev))); + goto done; + } + + pkt = mtod(m, struct atu_rxpkt *); + +#if 0 +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_tap(ifp->if_bpf, (u_int8_t *)&pkt->WiHeader, + pkt->AtHeader.wlength); +#if 0 + if (ifp->if_bpf) { + printf("HELKLO@#\n"); + BPF_MTAP(ifp, m); + } +#endif +#endif +#endif + +#if 0 + /* drop the raw 802.11b packets to bpf (including 4-byte FCS) */ + if (sc->atu_rawbpf) { + /* + * TODO: could drop mbuf's to bpf + */ + DPRINTF(("%s: bpf raw tap on RX :)\n", + USBDEVNAME(sc->atu_dev))); + + bpf_tap(sc->atu_rawbpf, (u_int8_t *)&pkt->WiHeader, + pkt->AtHeader.wlength); + } +#endif +#if 0 + /* drop the raw 802.11b packets to bpf (with aironet header) */ + if (sc->atu_airobpf) { + DPRINTF(("%s: bpf aironet tap on RX\n", + USBDEVNAME(sc->atu_dev))); + + atu_airo_tap(sc, (u_int8_t *)&pkt->WiHeader, + pkt->AtHeader.wlength - 4, &pkt->AtHeader); + } +#endif + + DPRINTFN(10, ("%s: -- RX (rate=%d enc=%d) mac=%s", + USBDEVNAME(sc->atu_dev), pkt->AtHeader.rx_rate, + (pkt->WiHeader.frame_ctl & WI_FCTL_WEP) != 0, + ether_sprintf(pkt->WiHeader.addr3))); + DPRINTFN(10, (" bssid=%s\n", ether_sprintf(pkt->WiHeader.addr2))); + + if (pkt->WiHeader.frame_ctl & WI_FCTL_WEP) { + DPRINTF(("%s: WEP enabled on RX\n", + USBDEVNAME(sc->atu_dev))); + } + + /* Is it a managment packet? */ + if ((pkt->WiHeader.frame_ctl & WI_FCTL_FTYPE) == WI_FTYPE_MGMT) { + atu_handle_mgmt_packet(sc, pkt); + goto done; + } + + /* Everything but data packets we just ignore from here */ + if ((pkt->WiHeader.frame_ctl & WI_FCTL_FTYPE) != WI_FTYPE_DATA) { + DPRINTF(("%s: ---- not a data packet? ---\n", + USBDEVNAME(sc->atu_dev))); + goto done; + } + + /* Woohaa! It's an ethernet packet! */ + DPRINTF(("%s: received a packet! rx-rate: %d\n", + USBDEVNAME(sc->atu_dev), pkt->AtHeader.rx_rate)); + + /* drop non-encrypted packets if wep-mode=on */ + if ((!(pkt->WiHeader.frame_ctl & WI_FCTL_WEP)) && + (sc->atu_encrypt & ATU_WEP_RX)) { + DPRINTF(("%s: dropping RX packet. (wep=off)\n", + USBDEVNAME(sc->atu_dev))); + goto done; + } + + DPRINTF(("%s: rx frag:%02x rssi:%02x q:%02x nl:%02x " + "time:%d\n", USBDEVNAME(sc->atu_dev), pkt->AtHeader.fragmentation, + pkt->AtHeader.rssi, pkt->AtHeader.link_quality, + pkt->AtHeader.noise_level, pkt->AtHeader.rx_time)); + + /* Do some sanity checking... */ + if (total_len < sizeof(struct ether_header)) { + DPRINTF(("%s: Packet too small?? (size:%d)\n", + USBDEVNAME(sc->atu_dev), total_len)); + ifp->if_ierrors++; + goto done; + } + /* if (total_len > 1514) { */ + if (total_len > 1548) { + DPRINTF(("%s: AAARRRGGGHHH!! Invalid packet size? (%d)\n", + USBDEVNAME(sc->atu_dev), total_len)); + ifp->if_ierrors++; + goto done; + } + + /* + * Copy src & dest mac to the right place (overwriting part of the + * 802.11 header) + */ + eth_hdr = (struct ether_header *)(pkt->Packet - 2 * ETHER_ADDR_LEN); + + switch (pkt->WiHeader.frame_ctl & (WI_FCTL_TODS | WI_FCTL_FROMDS)) { + case 0: + /* ad-hoc: copy order doesn't matter here */ + memcpy(eth_hdr->ether_shost, pkt->WiHeader.addr2, + ETHER_ADDR_LEN); + memcpy(eth_hdr->ether_dhost, pkt->WiHeader.addr1, + ETHER_ADDR_LEN); + break; + + case WI_FCTL_FROMDS: + /* infra mode: MUST be done in this order! */ + memcpy(eth_hdr->ether_shost, pkt->WiHeader.addr3, + ETHER_ADDR_LEN); + memcpy(eth_hdr->ether_dhost, pkt->WiHeader.addr1, + ETHER_ADDR_LEN); + + DPRINTF(("%s: infra decap (%d bytes)\n", + USBDEVNAME(sc->atu_dev), pkt->AtHeader.wlength)); + DPRINTF(("%s: RX: %50D\n", USBDEVNAME(sc->atu_dev), + (u_int8_t *)&pkt->WiHeader, " ")); + break; + + default: + DPRINTF(("%s: we shouldn't receive this (f_cntl=%02x)\n", + USBDEVNAME(sc->atu_dev), pkt->WiHeader.frame_ctl)); + } + + /* calculate 802.3 packet length (= packet - 802.11 hdr - fcs) */ + total_len = pkt->AtHeader.wlength - sizeof(struct wi_80211_hdr) + + 2 * ETHER_ADDR_LEN - 4; + + ifp->if_ipackets++; + m->m_pkthdr.rcvif = &sc->arpcom.ac_if; + + /* Adjust mbuf for headers */ + offset = sizeof(struct at76c503_rx_buffer) + + sizeof(struct wi_80211_hdr) - 12; + m->m_pkthdr.len = m->m_len = total_len + offset; + m_adj(m, offset); + + s = splnet(); + + if (atu_newbuf(sc, c, NULL) == ENOBUFS) { + ifp->if_ierrors++; + goto done1; + } + +#if NBPFILTER > 0 + if (ifp->if_bpf) + BPF_MTAP(ifp, m); +#endif + + IF_INPUT(ifp, m); + +done1: + splx(s); +done: + /* Put transfer back into rx-chain */ + SLIST_INSERT_HEAD(&sc->atu_cdata.atu_rx_free, c, atu_list); + + /* Setup new transfer. */ + usbd_setup_xfer(c->atu_xfer, sc->atu_ep[ATU_ENDPT_RX], + c, mtod(c->atu_mbuf, char *), ATU_RX_BUFSZ, + USBD_SHORT_XFER_OK, 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_softc *sc; + struct atu_chain *c; + struct ifnet *ifp; + usbd_status err; + int s; + + c = priv; + sc = c->atu_sc; + s = splusb(); + + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + c->atu_in_xfer = 0; + + if (c->atu_mbuf != NULL) { + /* put it back on the tx_list */ + sc->atu_cdata.atu_tx_inuse--; + SLIST_INSERT_HEAD(&sc->atu_cdata.atu_tx_free, c, + atu_list); + } else { + /* put it back on the mgmt_list */ + SLIST_INSERT_HEAD(&sc->atu_cdata.atu_mgmt_free, c, + atu_list); + } + /* + * turn off active flag if we're done transmitting. + * we don't depend on the active flag anywhere. do we still need to + * set it then? + */ + if (sc->atu_cdata.atu_tx_inuse == 0) { + ifp->if_flags &= ~IFF_OACTIVE; + } + DPRINTF(("%s: txeof me=%d status=%d\n", USBDEVNAME(sc->atu_dev), + c->atu_idx, status)); + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { + splx(s); + return; + } + + DPRINTF(("%s: usb error on tx: %s\n", USBDEVNAME(sc->atu_dev), + usbd_errstr(status))); + if (status == USBD_STALLED) + usbd_clear_endpoint_stall( + sc->atu_ep[ATU_ENDPT_TX]); + splx(s); + return; + } + + usbd_get_xfer_status(c->atu_xfer, NULL, NULL, NULL, &err); + + if (c->atu_mbuf != NULL) { + m_freem(c->atu_mbuf); + c->atu_mbuf = NULL; + if (ifp->if_snd.ifq_head != NULL) + atu_start(ifp); + } + + if (err) + ifp->if_oerrors++; + else + ifp->if_opackets++; + splx(s); +} + +#ifdef ATU_TX_PADDING +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; +} +#endif /* ATU_TX_PADDING */ + +int +atu_encap(struct atu_softc *sc, struct mbuf *m, struct atu_chain *c) +{ + int total_len; + struct atu_txpkt *pkt; + struct ether_header *eth_hdr; +#ifdef ATU_TX_PADDING + u_int8_t padding; +#endif /* ATU_TX_PADDING */ + + /* + * Copy the mbuf data into a contiguous buffer, leaving + * enough room for the atmel & 802.11 headers + */ + total_len = m->m_pkthdr.len; + + m_copydata(m, 0, m->m_pkthdr.len, c->atu_buf + + sizeof(pkt->AtHeader) + sizeof(struct wi_80211_hdr) - + 2 * ETHER_ADDR_LEN); + + total_len += sizeof(struct wi_80211_hdr) - 2 * ETHER_ADDR_LEN; + + pkt = (struct atu_txpkt *)c->atu_buf; + pkt->AtHeader.wlength = total_len; + /*pkt->AtHeader.tx_rate = 4; rate = auto */ + /* DAAN Daan daan !!! */ + pkt->AtHeader.tx_rate = 0; + pkt->AtHeader.padding = 0; + memset(pkt->AtHeader.reserved, 0x00, 4); + + pkt->WiHeader.dur_id = 0x0000; /* ? */ + pkt->WiHeader.frame_ctl = WI_FTYPE_DATA; + + eth_hdr = (struct ether_header *)(pkt->Packet - 2 * ETHER_ADDR_LEN); + + switch(sc->atu_mode) { + case AD_HOC_MODE: + /* dest */ + memcpy(pkt->WiHeader.addr1, eth_hdr->ether_dhost, + ETHER_ADDR_LEN); + /* src */ + memcpy(pkt->WiHeader.addr2, eth_hdr->ether_shost, + ETHER_ADDR_LEN); + /* bssid */ + memcpy(pkt->WiHeader.addr3, sc->atu_bssid, ETHER_ADDR_LEN); + DPRINTF(("%s: adhoc encap (bssid=%s)\n", + USBDEVNAME(sc->atu_dev), ether_sprintf(sc->atu_bssid))); + break; + + case INFRASTRUCTURE_MODE: + pkt->WiHeader.frame_ctl|=WI_FCTL_TODS; + /* bssid */ + memcpy(pkt->WiHeader.addr1, sc->atu_bssid, ETHER_ADDR_LEN); + /* src */ + memcpy(pkt->WiHeader.addr2, eth_hdr->ether_shost, + ETHER_ADDR_LEN); + /* dst */ + memcpy(pkt->WiHeader.addr3, eth_hdr->ether_dhost, + ETHER_ADDR_LEN); + + DPRINTF(("%s: infra encap (bssid=%s)\n", + USBDEVNAME(sc->atu_dev), ether_sprintf(sc->atu_bssid))); + } + memset(pkt->WiHeader.addr4, 0x00, ETHER_ADDR_LEN); + pkt->WiHeader.seq_ctl = 0; + + if (sc->atu_encrypt & ATU_WEP_TX) { + pkt->WiHeader.frame_ctl |= WI_FCTL_WEP; + DPRINTF(("%s: turning WEP on on packet\n", + USBDEVNAME(sc->atu_dev))); + } + + total_len += sizeof(pkt->AtHeader); +#ifdef ATU_TX_PADDING + padding = atu_calculate_padding(total_len % 64); + total_len += padding; + pkt->AtHeader.padding = padding; +#endif /* ATU_TX_PADDING */ + c->atu_length = total_len; + c->atu_mbuf = m; + return(0); +} + +void +atu_start(struct ifnet *ifp) +{ + struct atu_softc *sc = ifp->if_softc; + struct mbuf *m_head = NULL; + struct atu_cdata *cd; + struct atu_chain *entry; + usbd_status err; + int s; + + s = splnet(); + if (ifp->if_flags & IFF_OACTIVE) { + splx(s); + return; + } + + IFQ_POLL(&ifp->if_snd, m_head); + if (m_head == NULL) { + splx(s); + return; + } + + entry = SLIST_FIRST(&sc->atu_cdata.atu_tx_free); + while (entry) { + if (entry == NULL) { + /* all transfers are in use at this moment */ + splx(s); + return; + } + + if (sc->atu_mgmt_vars.state != STATE_HAPPY_NETWORKING) { + /* don't try to send if we're not associated */ + splx(s); + return; + } + + cd = &sc->atu_cdata; + + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) { + /* no packets on queue */ + splx(s); + return; + } + + SLIST_REMOVE_HEAD(&sc->atu_cdata.atu_tx_free, atu_list); + + ifp->if_flags |= IFF_OACTIVE; + cd->atu_tx_inuse++; + + DPRINTF(("%s: index:%d (inuse=%d)\n", + USBDEVNAME(sc->atu_dev), entry->atu_idx, cd->atu_tx_inuse)); + + err = atu_encap(sc, m_head, entry); + if (err) { + DPRINTF(("%s: error encapsulating packet!\n", + USBDEVNAME(sc->atu_dev))); + IF_PREPEND(&ifp->if_snd, m_head); + if (--cd->atu_tx_inuse == 0) + ifp->if_flags &= ~IFF_OACTIVE; + splx(s); + return; + } + err = atu_send_packet(sc, entry); + if (err) { + DPRINTF(("%s: error sending packet!\n", + USBDEVNAME(sc->atu_dev))); + splx(s); + return; + } + +#if NBPFILTER > 0 +#if 0 + if (sc->atu_airobpf) + bpf_mtap(sc->atu_airobpf, m_head); +#endif + + if (ifp->if_bpf) + BPF_MTAP(ifp, m_head); +#endif + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + entry = SLIST_FIRST(&sc->atu_cdata.atu_tx_free); + } + splx(s); +} + +void +atu_init(void *xsc) +{ + struct atu_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct atu_chain *c; + struct atu_cdata *cd = &sc->atu_cdata; + usbd_status err; + int i, s; + + s = splnet(); + + DPRINTF(("%s: atu_init\n", USBDEVNAME(sc->atu_dev))); + + if (ifp->if_flags & IFF_RUNNING) { + splx(s); + return; + } + + /* Init TX ring */ + if (atu_xfer_list_init(sc, cd->atu_tx_chain, ATU_TX_LIST_CNT, 0, + ATU_TX_BUFSZ, &cd->atu_tx_free)) { + DPRINTF(("%s: tx list init failed\n", USBDEVNAME(sc->atu_dev))); + } + + /* Init RX ring */ + if (atu_xfer_list_init(sc, cd->atu_rx_chain, ATU_RX_LIST_CNT, 1, + 0, &cd->atu_rx_free)) { + DPRINTF(("%s: rx list init failed\n", USBDEVNAME(sc->atu_dev))); + } + + /* Init mgmt ring */ + if (atu_xfer_list_init(sc, cd->atu_mgmt_chain, + ATU_MGMT_LIST_CNT, 0, ATU_MGMT_BUFSZ, &cd->atu_mgmt_free)) { + DPRINTF(("%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; + } + + 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; + } + + /* 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, mtod(c->atu_mbuf, char *), ATU_RX_BUFSZ, + USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, atu_rxeof); + usbd_transfer(c->atu_xfer); + } + + bcopy((char *)&sc->arpcom.ac_enaddr, sc->atu_mac_addr, + ETHER_ADDR_LEN); + DPRINTF(("%s: starting up using MAC=%s\n", + USBDEVNAME(sc->atu_dev), ether_sprintf(sc->atu_mac_addr))); + + /* Do initial setup */ + err = atu_initial_config(sc); + if (err) { + DPRINTF(("%s: initial config failed!\n", + USBDEVNAME(sc->atu_dev))); + splx(s); + return; + } + DPRINTF(("%s: initialised transceiver\n", + USBDEVNAME(sc->atu_dev))); + + /* Fire up managment task */ + DPRINTF(("%s: trying to start mgmt task...\n", + USBDEVNAME(sc->atu_dev))); + if (!(sc->atu_mgmt_flags & ATU_TASK_RUNNING)) { + sc->atu_dying = 0; + err = kthread_create(atu_mgmt_loop, sc, + &sc->atu_mgmt_thread, USBDEVNAME(sc->atu_dev)); + if (err) { + DPRINTF(("%s: failed to create kthread\n", + USBDEVNAME(sc->atu_dev))); + } + + sc->atu_mgmt_flags |= ATU_TASK_RUNNING; + } + + /* 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; + */ + + sc->atu_mgmt_flags |= ATU_CHANGED_SETTINGS; + wakeup(sc); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + splx(s); +} + +void +atu_print_a_bunch_of_debug_things(struct atu_softc *sc) +{ + usbd_status err; + u_int8_t tmp[32]; + + /* DEBUG */ + err = atu_get_mib(sc, MIB_MAC_MGMT__CURRENT_BSSID, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: current BSSID=%s\n", + USBDEVNAME(sc->atu_dev), ether_sprintf(tmp))); + + err = atu_get_mib(sc, MIB_MAC_MGMT__BEACON_PERIOD, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: beacon period=%d\n", + USBDEVNAME(sc->atu_dev), tmp[0])); + + err = atu_get_mib(sc, MIB_MAC_WEP__PRIVACY_INVOKED, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: privacy invoked=%d\n", + USBDEVNAME(sc->atu_dev), tmp[0])); + + err = atu_get_mib(sc, MIB_MAC_WEP__ENCR_LEVEL, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: encr_level=%d\n", + USBDEVNAME(sc->atu_dev), tmp[0])); + + err = atu_get_mib(sc, MIB_MAC_WEP__ICV_ERROR_COUNT, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: icv error count=%d\n", + USBDEVNAME(sc->atu_dev), *(short *)tmp)); + + err = atu_get_mib(sc, MIB_MAC_WEP__EXCLUDED_COUNT, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: wep excluded count=%d\n", + USBDEVNAME(sc->atu_dev), *(short *)tmp)); + + err = atu_get_mib(sc, MIB_MAC_MGMT__POWER_MODE, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: power mode=%d\n", USBDEVNAME(sc->atu_dev), + tmp[0])); + + err = atu_get_mib(sc, MIB_PHY__CHANNEL, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: channel=%d\n", USBDEVNAME(sc->atu_dev), + tmp[0])); + + err = atu_get_mib(sc, MIB_PHY__REG_DOMAIN, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: reg domain=%d\n", USBDEVNAME(sc->atu_dev), + tmp[0])); + + err = atu_get_mib(sc, MIB_LOCAL__SSID_SIZE, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: ssid size=%d\n", USBDEVNAME(sc->atu_dev), + tmp[0])); + + err = atu_get_mib(sc, MIB_LOCAL__BEACON_ENABLE, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: beacon enable=%d\n", + USBDEVNAME(sc->atu_dev), tmp[0])); + + err = atu_get_mib(sc, MIB_LOCAL__AUTO_RATE_FALLBACK, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: auto rate fallback=%d\n", + USBDEVNAME(sc->atu_dev), tmp[0])); + + err = atu_get_mib(sc, MIB_MAC_ADDR__ADDR, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: mac addr=%s\n", USBDEVNAME(sc->atu_dev), + ether_sprintf(tmp))); + + err = atu_get_mib(sc, MIB_MAC__DESIRED_SSID, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: desired ssid=%s\n", + USBDEVNAME(sc->atu_dev), tmp)); + + err = atu_get_mib(sc, MIB_MAC_MGMT__CURRENT_ESSID, tmp); + if (err) return; + DPRINTF(("%s: DEBUG: current ESSID=%s\n", + USBDEVNAME(sc->atu_dev), tmp)); + +} + +int +atu_set_wepkey(struct atu_softc *sc, int nr, u_int8_t *key, int len) +{ + if ((len != 5) && (len != 13)) + return EINVAL; + + DPRINTF(("%s: changed wepkey %d (len=%d)\n", + USBDEVNAME(sc->atu_dev), nr, len)); + + memcpy(sc->atu_wepkeys[nr], key, len); + if (len == 13) + sc->atu_wepkeylen = ATU_WEP_104BITS; + else + sc->atu_wepkeylen = ATU_WEP_40BITS; + + atu_send_mib(sc, MIB_MAC_WEP__ENCR_LEVEL, NR(sc->atu_wepkeylen)); + return atu_send_mib(sc, MIB_MAC_WEP__KEYS(nr), key); +} + +int +atu_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct atu_softc *sc = ifp->if_softc; + + struct ifreq *ifr = (struct ifreq *)data; + struct ifaddr *ifa = (struct ifaddr *)data; + struct ieee80211req *ireq; + int err = 0; +#if 0 + u_int8_t tmp[32] = ""; + int len = 0; +#endif + struct ieee80211_nwid nwid; + struct wi_req wreq; + int change, s; + + s = splnet(); + ireq = (struct ieee80211req *)data; + change = ifp->if_flags ^ sc->atu_if_flags; + + DPRINTF(("atu_ioctl: command=%lu\n", command)); + + switch (command) { + case SIOCSIFADDR: + DPRINTF(("%s: SIOCSIFADDR\n", USBDEVNAME(sc->atu_dev))); + + ifp->if_flags |= IFF_UP; + atu_init(sc); + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + arp_ifinit(&sc->arpcom, ifa); + break; +#endif /* INET */ + } + break; + + + case SIOCSIFMTU: + if (ifr->ifr_mtu > ATU_MAX_MTU) + err = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + + case SIOCSIFFLAGS: + DPRINTF(("%s: SIOCSIFFLAGS\n", USBDEVNAME(sc->atu_dev))); + + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->atu_if_flags & IFF_PROMISC)) { +/* enable promisc */ +#if 0 + sc->atu_rxfilt |= ATU_RXFILT_PROMISC; + atu_setword(sc, ATU_CMD_SET_PKT_FILTER, + sc->atu_rxfilt); +#endif + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->atu_if_flags & IFF_PROMISC) { +/* disable promisc */ +#if 0 + sc->atu_rxfilt &= ~ATU_RXFILT_PROMISC; + atu_setword(sc, ATU_CMD_SET_PKT_FILTER, + sc->atu_rxfilt); +#endif + } else if (!(ifp->if_flags & IFF_RUNNING)) + atu_init(sc); + +#if 0 + DPRINTF(("%s: ioctl calling " + "atu_init()\n", USBDEVNAME(sc->atu_dev))); + atu_init(sc); + err = atu_switch_radio(sc, 1); +#endif + } else { + if (ifp->if_flags & IFF_RUNNING) + atu_stop(sc); + err = atu_switch_radio(sc, 0); + } + sc->atu_if_flags = ifp->if_flags; + + err = 0; + break; + + case SIOCADDMULTI: + DPRINTF(("%s: SIOCADDMULTI\n", USBDEVNAME(sc->atu_dev))); + /* TODO: implement */ + err = 0; + break; + + case SIOCDELMULTI: + DPRINTF(("%s: SIOCDELMULTI\n", USBDEVNAME(sc->atu_dev))); + /* TODO: implement */ + err = 0; + break; + + case SIOCS80211NWID: + DPRINTF(("%s: SIOCS80211NWID\n", USBDEVNAME(sc->atu_dev))); + err = copyin(ifr->ifr_data, &nwid, sizeof(nwid)); + if (err) + break; + if (nwid.i_len > IEEE80211_NWID_LEN) { + err = EINVAL; + break; + } + sc->atu_ssidlen = nwid.i_len; + memcpy(sc->atu_ssid, nwid.i_nwid, nwid.i_len); + sc->atu_mgmt_flags |= ATU_CHANGED_SETTINGS; + wakeup(sc); + break; + + case SIOCG80211NWID: + DPRINTF(("%s: SIOGS80211NWID\n", USBDEVNAME(sc->atu_dev))); + nwid.i_len = sc->atu_ssidlen; + memcpy(nwid.i_nwid, sc->atu_ssid, nwid.i_len); + err = copyout(&nwid, ifr->ifr_data, sizeof(nwid)); + atu_print_a_bunch_of_debug_things(sc); + break; + +#if 0 + case SIOCG80211: + switch(ireq->i_type) { + case IEEE80211_IOC_SSID: + err = copyout(sc->atu_ssid, ireq->i_data, + sc->atu_ssidlen); + ireq->i_len = sc->atu_ssidlen; + break; + + case IEEE80211_IOC_NUMSSIDS: + ireq->i_val = 1; + break; + + case IEEE80211_IOC_CHANNEL: + ireq->i_val = sc->atu_channel; + + /* + * every time the channel is requested, we errr... + * print a bunch of debug things :) + */ + atu_print_a_bunch_of_debug_things(sc); + break; + + case IEEE80211_IOC_AUTHMODE: + /* TODO: change this when shared-key is implemented */ + ireq->i_val = IEEE80211_AUTH_OPEN; + break; + + case IEEE80211_IOC_WEP: + switch (sc->atu_encrypt) { + case ATU_WEP_TX: + ireq->i_val = IEEE80211_WEP_MIXED; + break; + case ATU_WEP_TXRX: + ireq->i_val = IEEE80211_WEP_ON; + break; + default: + ireq->i_val = IEEE80211_WEP_OFF; + } + break; + + case IEEE80211_IOC_NUMWEPKEYS: + ireq->i_val = 4; + break; + + case IEEE80211_IOC_WEPKEY: + err = suser(curproc, 0); + if (err) + break; + + if((ireq->i_val < 0) || (ireq->i_val > 3)) { + err = EINVAL; + break; + } + + if (sc->atu_encrypt == ATU_WEP_40BITS) + len = 5; + else + len = 13; + + err = copyout(sc->atu_wepkeys[ireq->i_val], + ireq->i_data, len); + break; + + case IEEE80211_IOC_WEPTXKEY: + ireq->i_val = sc->atu_wepkey; + break; + + default: + DPRINTF(("%s: ioctl: unknown 80211: " + "%04x %d\n", USBDEVNAME(sc->atu_dev), ireq->i_type, + ireq->i_type)); + err = EINVAL; + } + break; + + case SIOCS80211: + err = suser(curproc, 0); + if (err) + break; + + switch(ireq->i_type) { + case IEEE80211_IOC_SSID: + if (ireq->i_len < 0 || ireq->i_len > 32) { + err = EINVAL; + break; + } + + err = copyin(ireq->i_data, tmp, ireq->i_len); + if (err) + break; + + sc->atu_ssidlen = ireq->i_len; + memcpy(sc->atu_ssid, tmp, ireq->i_len); + + sc->atu_mgmt_flags |= ATU_CHANGED_SETTINGS; + wakeup(sc); + break; + + case IEEE80211_IOC_CHANNEL: + if (ireq->i_val < 1 || ireq->i_val > 14) { + err = EINVAL; + break; + } + + sc->atu_channel = ireq->i_val; + + /* restart scan / join / etc now */ + sc->atu_mgmt_flags |= ATU_CHANGED_SETTINGS; + wakeup(sc); + break; + + case IEEE80211_IOC_WEP: + switch (ireq->i_val) { + case IEEE80211_WEP_OFF: + sc->atu_encrypt = ATU_WEP_OFF; + break; + case IEEE80211_WEP_MIXED: + sc->atu_encrypt = ATU_WEP_TX; + break; + case IEEE80211_WEP_ON: + sc->atu_encrypt = ATU_WEP_TXRX; + break; + default: + err = EINVAL; + } + if (err) + break; + + /* + * to change the wep-bit in our beacon we HAVE to send + * CMD_STARTUP again + */ + err = atu_initial_config(sc); + /* + * after that we have to send CMD_JOIN again to get + * the receiver running again. so we'll just + * restart the entire join/assoc/auth state-machine. + */ + sc->atu_mgmt_flags |= ATU_CHANGED_SETTINGS; + wakeup(sc); + break; + + case IEEE80211_IOC_WEPKEY: + if ((ireq->i_val < 0) || (ireq->i_val > 3) || + (ireq->i_len > 13)) { + err = EINVAL; + break; + } + err = copyin(ireq->i_data, tmp, ireq->i_len); + if (err) + break; + err = atu_set_wepkey(sc, ireq->i_val, tmp, + ireq->i_len); + break; + + case IEEE80211_IOC_WEPTXKEY: + if ((ireq->i_val < 0) || (ireq->i_val > 3)) { + err = EINVAL; + break; + } + sc->atu_wepkey = ireq->i_val; + err = atu_send_mib(sc, MIB_MAC_WEP__KEY_ID, + NR(sc->atu_wepkey)); + + break; + + case IEEE80211_IOC_AUTHMODE: + /* TODO: change when shared-key is implemented */ + if (ireq->i_val != IEEE80211_AUTH_OPEN) + err = EINVAL; + break; + + default: + err = EINVAL; + } + break; +#endif + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + err = ifmedia_ioctl(ifp, ifr, &sc->atu_media, command); + break; + + case SIOCGWAVELAN: + DPRINTF(("%s: ioctl: get wavelan\n", + USBDEVNAME(sc->atu_dev))); + /* + err = ether_ioctl(ifp, &sc->arpcom, command, data); + break; + */ + + /* TODO: implement */ + + err = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); + if (err) + break; + + DPRINTF(("%s: SIOCGWAVELAN\n", USBDEVNAME(sc->atu_dev))); + if (wreq.wi_len > WI_MAX_DATALEN) { + err = EINVAL; + break; + } + + DPRINTF(("%s: ioctl: wi_type=%04x %d\n", + USBDEVNAME(sc->atu_dev), wreq.wi_type, wreq.wi_type)); + err = 0; + /* err = EINVAL; */ + break; + + case SIOCSWAVELAN: + DPRINTF(("%s: ioctl: wavset type=%x\n", + USBDEVNAME(sc->atu_dev), 0)); + err = 0; + break; + + default: + DPRINTF(("%s: ioctl: default\n", + USBDEVNAME(sc->atu_dev))); + err = ether_ioctl(ifp, &sc->arpcom, command, data); + break; + } + + sc->atu_if_flags = ifp->if_flags; + splx(s); + return(err); +} + +void +atu_watchdog(struct ifnet *ifp) +{ + struct atu_softc *sc; + struct atu_chain *c; + usbd_status stat; + int cnt, s; + + 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); + } + } + for (cnt = 0; cnt < ATU_MGMT_LIST_CNT; cnt++) { + c = &sc->atu_cdata.atu_mgmt_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 (ifp->if_snd.ifq_head != NULL) + atu_start(ifp); + splx(s); +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +void +atu_stop(struct atu_softc *sc) +{ + usbd_status err; + struct ifnet *ifp; + struct atu_cdata *cd; + int s; + + s = splnet(); + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + /* there must be a better way to clean up the mgmt task... */ + sc->atu_dying = 1; + splx(s); + + if (sc->atu_mgmt_flags & ATU_TASK_RUNNING) { + DPRINTF(("%s: waiting for mgmt task to die\n", + USBDEVNAME(sc->atu_dev))); + wakeup(sc); + while (sc->atu_dying == 1) { + atu_msleep(sc, 100); + } + } + s = splnet(); + DPRINTF(("%s: stopped managment thread\n", + USBDEVNAME(sc->atu_dev))); + + /* 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); + atu_xfer_list_free(sc, cd->atu_mgmt_chain, ATU_MGMT_LIST_CNT); + + /* Let's be nice and turn off the radio before we leave */ + atu_switch_radio(sc, 0); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + splx(s); +} diff --git a/sys/dev/usb/if_atureg.h b/sys/dev/usb/if_atureg.h new file mode 100644 index 00000000000..49dad14337d --- /dev/null +++ b/sys/dev/usb/if_atureg.h @@ -0,0 +1,723 @@ +/* $OpenBSD: if_atureg.h,v 1.1 2004/11/08 22:09:11 dlg Exp $ */ +/* + * Copyright (c) 2003 + * 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. + * + */ + +/* $ATUWI: $Id: if_atureg.h,v 1.1 2004/11/08 22:09:11 dlg Exp $ */ + +/************ driver options ************/ + +/* + * If you want to disable Intersil or RFMD support in the driver, uncomment + * one of the following line. This will make the driver about 30 KB smaller + * since the firmware-images will be left out. (Yes, leaving both out saves + * 60KB, but that leaves you with a driver that doesn't support any devices :) + */ +/* #define ATU_NO_INTERSIL */ +/* #define ATU_NO_RFMD */ +/* #define ATU_NO_RFMD2958 */ +/* #define ATU_NO_RFMD2958_SMC */ + + +/* debug flags used in the driver - don't change */ +#define FLAG_COMMANDS 0x00001 /* Atmel USB commands */ +#define FLAG_BEACONS 0x00002 /* reception of beacons */ +#define FLAG_BEACONSFULL 0x00004 /* verbose */ +#define FLAG_SIGNAL 0x00008 /* signal strength of packets */ +#define FLAG_TX 0x00010 /* transmittion of packets */ +#define FLAG_RX 0x00020 /* reception of packets */ +#define FLAG_RXFULL 0x00040 /* verbose */ +#define FLAG_MGMT 0x00080 /* managment packets */ +#define FLAG_MGMTFULL 0x00100 /* verbose */ +#define FLAG_STATE 0x00200 /* scan/join/auth/assoc state */ +#define FLAG_INIT 0x00400 /* device initialisation / tear down */ +#define FLAG_FW 0x00800 /* firmware loader */ +#define FLAG_FWFULL 0x01000 /* verbose */ +#define FLAG_IOCTL 0x02000 /* ioctls */ +#define FLAG_BPF 0x04000 /* bpf */ +#define FLAG_ALL 0x07fff /* all of the above */ + + +#define ATU_CONFIG_NO 1 +#define ATU_IFACE_IDX 0 + +/************ various performance optimizations ************/ + +/* + * setting ATU_NO_OPTIMIZATIONS will set all further options to default + * values... + * + * no optimizations 392.78 KB/sec + * with optimizations 485.64 KB/sec + * (+/- 23.6% increase) + * + * Disclaimer : Most speed tests were done by transferring a large file over + * FTP with an MTU of 1500. The tests are done on slow (Pentium-133) machines + * so small changes in driver-overhead would be easilly noticeable. These + * numbers are different for every other PC, I have just put them here to show + * in what order of magnitude the differences are. + */ +/* #define ATU_NO_OPTIMIZATIONS */ + + +/* the number of simultaniuously requested RX transfers */ +#define ATU_RX_LIST_CNT 1 + +/* + * the number of simultaniously started TX transfers + * my measurements : + * 1 430.82 KB/sec + * 2 534.66 KB/sec + * 3 536.23 KB/sec + * 4 537.80 KB/sec + * 6 537.30 KB/sec + * 8 535.31 KB/sec + * 16 535.68 KB/sec + * 128 535.67 KB/sec (before you ask : yes, 128 is silly :) + * (+/- 24% increase) + */ +#define ATU_TX_LIST_CNT 8 + +/* number of simultaniously MGMT TX transfers (always 1, don't change) */ +#define ATU_MGMT_LIST_CNT 1 + +/* + * save a memcpy on TX by directly constructing packets into a self-allocated + * DMA buffer. (normally usbd allocates them and memcpy's data in on transfer) + * + * measurements : + * off 507.50 KB/sec + * on 530.14 KB/sec + * (+/- 4.5% increase) + */ +#define ATU_NO_COPY_TX + + +/* + * Normally, when a packet arrives at the driver (with a call to atu_start) + * we start up a USB transfer and transfer the packet to the adapter. Then + * atu_txeof gets called on interrupt and we hand over the mbuf to + * usb_tx_done. usb_tx_done will queue the mbuf and schedules a net-isr. + * After the interrupt the scheduler will call the net-isr. There the mbuf + * is free'd and atu_start is called (if there are packets on the TX queue). + * This means that on average 2 task-switches are performed for every single + * packet we transmit. + * If we simply NOT call usb_tx_done when a single transfer is done, but wait + * for the other transfers we started simultaneously to complete, we can save + * the overhead of the extra task-switching. + * + * measurements : + * off 465.55 KB/sec + * on 487.57 KB/sec + * (+/- 4.7% increase) + */ +#define ATU_LAZY_TX_NETISR + +/* + * Minimum number of simultaneous transfers before we start being lazy. + * Setting this to 0 will make us so lazy we could keep mbuf's around forever + * (which you do not want) + */ +#define ATU_LAZY_TX_MIN 1 + +/* + * Maximum number of queued mbufs before we call usb_tx_done (should never + * be higher than ATU_TX_LIST_CNT) + */ +#define ATU_LAZY_TX_MAX 8 + +/* + * Whether or not we padd usb packets transfered to the adapter. looking at + * drivers on other platforms there seems to be something magic about the + * padding. + * my measurements : + * on - 532.55 KB/sec + * off - 536.74 KB/sec + * I don't see the reason why we should padd bytes here. The adapter seems + * to be transmitting packets just fine without the padding. So the default is + * to have no padding, but it's at least supplied as an option. This seems to + * be necessary with newer firmware versions, but I haven't tested that yet. + */ +/* #define ATU_TX_PADDING */ + +/* + * ATU_PROFILING isn't a real optimization. it adds s bunch of sysctl + * variables that count the number of function calls and data transfers we + * make. with some shell scripts around it you can easilly measure the + * performance of the driver and see where bottlenecks might be. + */ +/* #define ATU_PROFILING */ + + + + +/**** NO user configurable options beyond this point ****/ + + +#ifdef ATU_NO_OPTIMIZATIONS +#undef ATU_LAZY_TX_NETISR +#undef ATU_TX_PADDING +#undef ATU_NO_COPY_TX +#undef ATU_TX_LIST_CNT +#define ATU_TX_LIST_CNT 1 +#undef ATU_RX_LIST_CNT +#define ATU_RX_LIST_CNT 1 +#endif + + + +/* + * According to the 802.11 spec (7.1.2) the frame body can be up to 2312 bytes + */ +#define ATU_RX_BUFSZ (sizeof(struct at76c503_rx_buffer) + \ + sizeof(struct wi_80211_hdr) + 2312 + 4) +/* BE CAREFULL! should add ATU_TX_PADDING */ +#define ATU_TX_BUFSZ (sizeof(struct at76c503_tx_buffer) + \ + sizeof(struct wi_80211_hdr) + 2312) +#define ATU_MGMT_BUFSZ (sizeof(struct at76c503_tx_buffer) + 300) + +#define ATU_MIN_FRAMELEN 60 + +/* + * Sending packets of more than 1500 bytes confuses some access points, so the + * default MTU is set to 1500 but can be increased up to 2310 bytes using + * ifconfig + */ +#define ATU_DEFAULT_MTU 1500 +#define ATU_MAX_MTU (2312 - 2) + + +#define ATU_ENDPT_RX 0x0 +#define ATU_ENDPT_TX 0x1 +#define ATU_ENDPT_MAX 0x2 + +#define ATU_TX_TIMEOUT 10000 + +#define ATU_JOIN_TIMEOUT 2000 + +#define ATU_MGMT_INTERVAL 1000 * hz / 1000 +#define ATU_SCAN_RETRIES 2 +#define ATU_JOIN_RETRIES 1 +#define ATU_AUTH_RETRIES 3 +#define ATU_ASSOC_RETRIES 3 +#define ATU_IBSS_RETRIES 0 + +#define ATU_NO_QUIRK 0x0000 +#define ATU_QUIRK_NO_REMAP 0x0001 +#define ATU_QUIRK_FW_DELAY 0x0002 + +#define ATU_DEFAULT_SSID "" + + + +enum atu_radio_type { + RadioRFMD = 0, + RadioRFMD2958, + RadioRFMD2958_SMC, + RadioIntersil +}; + +struct atu_type { + u_int16_t atu_vid; + u_int16_t atu_pid; + enum atu_radio_type atu_radio; + u_int16_t atu_quirk; +}; + + +struct atu_softc; + +struct atu_chain { + struct atu_softc *atu_sc; + usbd_xfer_handle atu_xfer; + char *atu_buf; + struct mbuf *atu_mbuf; + u_int8_t atu_idx; + u_int16_t atu_length; + int atu_in_xfer; +#ifdef ATU_LAZY_TX_NETISR + u_int8_t atu_not_freed; +#endif /* ATU_LAZY_TX_NETISR */ + SLIST_ENTRY(atu_chain) atu_list; +}; + +struct atu_cdata { + struct atu_chain atu_tx_chain[ATU_TX_LIST_CNT]; + struct atu_chain atu_rx_chain[ATU_RX_LIST_CNT]; + struct atu_chain atu_mgmt_chain[ATU_MGMT_LIST_CNT]; + + SLIST_HEAD(atu_list_head, atu_chain) atu_rx_free; + struct atu_list_head atu_tx_free; + struct atu_list_head atu_mgmt_free; + + u_int8_t atu_tx_inuse; + u_int8_t atu_tx_last_idx; +}; + + +#define MAX_SSID_LEN 32 +#define ATU_AVG_TIME 20 + + + +enum atu_mgmt_state { + STATE_NONE = 0, + STATE_LISTENING, + STATE_JOINING, + + STATE_AUTHENTICATING, /* infra mode */ + STATE_ASSOCIATING, + + STATE_CREATING_IBSS, /* adhoc mode */ + + STATE_HAPPY_NETWORKING, + STATE_GIVEN_UP +}; + + +u_int8_t *atu_mgmt_statename[] = {"NONE", "LISTENING", "JOINING", + "AUTHENTICATING", "ASSOCIATING", "CREATING IBSS", "HAPPY NETWORKING :)", + "GIVEN UP"}; + + +struct atu_mgmt { + enum atu_mgmt_state state; + int retry; +}; + + + +struct atu_softc { + USBBASEDEVICE atu_dev; + + struct arpcom arpcom; + usbd_device_handle atu_udev; + usbd_interface_handle atu_iface; + struct ifmedia atu_media; + int atu_ed[ATU_ENDPT_MAX]; + usbd_pipe_handle atu_ep[ATU_ENDPT_MAX]; + int atu_unit; + int atu_if_flags; + + struct atu_cdata atu_cdata; + +#ifdef ATU_LAZY_TX_NETISR + int atu_lazy_tx_cnt; + /* struct mbuf *atu_lazy_tx_list[ATU_LAZY_TX_MAX]; */ +#endif /* ATU_LAZY_TX_NETISR */ + + char atu_dying; + struct timeval atu_rx_notice; + caddr_t atu_rawbpf; + caddr_t atu_airobpf; + + u_int8_t atu_mac_addr[ETHER_ADDR_LEN]; + u_int8_t atu_bssid[ETHER_ADDR_LEN]; + enum atu_radio_type atu_radio; + u_int16_t atu_quirk; + + /* used for debug : FLAG_SIGNAL */ + u_int8_t atu_signalarr[ATU_AVG_TIME]; + u_int8_t atu_signalptr; + u_int16_t atu_signaltotal; + + u_int8_t atu_ssid[MAX_SSID_LEN]; + u_int8_t atu_ssidlen; + u_int8_t atu_channel; + u_int8_t atu_mode; +#define NO_MODE_YET 0 +#define AD_HOC_MODE 1 +#define INFRASTRUCTURE_MODE 2 + + u_int8_t atu_radio_on; + u_int8_t atu_encrypt; +#define ATU_WEP_RX 0x01 +#define ATU_WEP_TX 0x02 +#define ATU_WEP_TXRX (0x01 | 0x02) + int atu_wepkey; + int atu_wepkeylen; + u_int8_t atu_wepkeys[4][13]; + + struct proc *atu_mgmt_thread; + struct atu_mgmt atu_mgmt_vars; + u_int16_t atu_mgmt_flags; +#define ATU_TASK_RUNNING 0x01 +#define ATU_CHANGED_SETTINGS 0x02 +#define ATU_SEARCHING 0x04 +#define ATU_FOUND_BSSID 0x08 +#define ATU_AUTH_OK 0x10 +#define ATU_RE_AUTH 0x20 +#define ATU_ASSOC_OK 0x40 +#define ATU_RE_ASSOC 0x80 +#define ATU_NETWORK_OK 0x100 +}; + + + + + + +/* Commands for uploading the firmware (standard DFU interface) */ +#define DFU_DNLOAD UT_WRITE_CLASS_INTERFACE, 0x01 +#define DFU_GETSTATUS UT_READ_CLASS_INTERFACE, 0x03 +#define DFU_GETSTATE UT_READ_CLASS_INTERFACE, 0x05 +#define DFU_REMAP UT_WRITE_VENDOR_INTERFACE, 0x0a + + +/* DFU states */ +#define DFUState_AppIdle 0 +#define DFUState_AppDetach 1 +#define DFUState_DFUIdle 2 +#define DFUState_DnLoadSync 3 +#define DFUState_DnLoadBusy 4 +#define DFUState_DnLoadIdle 5 +#define DFUState_ManifestSync 6 +#define DFUState_Manifest 7 +#define DFUState_ManifestWait 8 +#define DFUState_UploadIdle 9 +#define DFUState_DFUError 10 + +#define DFU_MaxBlockSize 1024 + + +/* AT76c503 operating modes */ +#define MODE_NONE 0x00 +#define MODE_NETCARD 0x01 +#define MODE_CONFIG 0x02 +#define MODE_DFU 0x03 +#define MODE_NOFLASHNETCARD 0x04 + + + + +/* AT76c503 commands */ +#define CMD_SET_MIB 0x01 +#define CMD_START_SCAN 0x03 +#define CMD_JOIN 0x04 +#define CMD_START_IBSS 0x05 +#define CMD_RADIO 0x06 +#define CMD_RADIO_ON 0x06 +#define CMD_RADIO_OFF 0x07 +#define CMD_STARTUP 0x0b + +/* AT76c503 status messages - used in atu_wait_completion */ +#define STATUS_IDLE 0x00 +#define STATUS_COMPLETE 0x01 +#define STATUS_UNKNOWN 0x02 +#define STATUS_INVALID_PARAMETER 0x03 +#define STATUS_FUNCTION_NOT_SUPPORTED 0x04 +#define STATUS_TIME_OUT 0x07 +#define STATUS_IN_PROGRESS 0x08 +#define STATUS_HOST_FAILURE 0xff +#define STATUS_SCAN_FAILED 0xf0 + + + +/* AT76c503 command header */ +struct atu_cmd { + u_int8_t Cmd; + u_int8_t Reserved; + u_int16_t Size; +}; + +/* CMD_SET_MIB command (0x01) */ +struct atu_cmd_set_mib { + /* AT76c503 command header */ + u_int8_t AtCmd; + u_int8_t AtReserved; + u_int16_t AtSize; + + /* MIB header */ + u_int8_t MIBType; + u_int8_t MIBSize; + u_int8_t MIBIndex; + u_int8_t MIBReserved; + + /* MIB data */ + u_int8_t data[72]; +}; + +/* CMD_STARTUP command (0x0b) */ +struct atu_cmd_card_config { + u_int8_t Cmd; + u_int8_t Reserved; + u_int16_t Size; + + u_int8_t ExcludeUnencrypted; + u_int8_t PromiscuousMode; + u_int8_t ShortRetryLimit; + u_int8_t EncryptionType; + u_int16_t RTS_Threshold; + u_int16_t FragThreshold; /* 256 .. 2346 */ + u_int8_t BasicRateSet[4]; + u_int8_t AutoRateFallback; + u_int8_t Channel; + u_int8_t PrivacyInvoked; /* wep */ + u_int8_t WEP_DefaultKeyID; /* 0 .. 3 */ + u_int8_t SSID[MAX_SSID_LEN]; + u_int8_t WEP_DefaultKey[4][13]; + u_int8_t SSID_Len; + u_int8_t ShortPreamble; + u_int16_t BeaconPeriod; +}; + +/* CMD_SCAN command (0x03) */ +struct atu_cmd_do_scan { + u_int8_t Cmd; + u_int8_t Reserved; + u_int16_t Size; + + u_int8_t BSSID[ETHER_ADDR_LEN]; + u_int8_t SSID[MAX_SSID_LEN]; + u_int8_t ScanType; + u_int8_t Channel; + u_int16_t ProbeDelay; + u_int16_t MinChannelTime; + u_int16_t MaxChannelTime; + u_int8_t SSID_Len; + u_int8_t InternationalScan; +}; + +#define ATU_SCAN_ACTIVE 0x00 +#define ATU_SCAN_PASSIVE 0x01 + +/* CMD_JOIN command (0x04) */ +struct atu_cmd_join { + u_int8_t Cmd; + u_int8_t Reserved; + u_int16_t Size; + + u_int8_t bssid[ETHER_ADDR_LEN]; + u_int8_t essid[32]; + u_int8_t bss_type; + u_int8_t channel; + u_int16_t timeout; + u_int8_t essid_size; + u_int8_t reserved; +}; + +/* CMD_START_IBSS (0x05) */ +struct atu_cmd_start_ibss { + u_int8_t Cmd; + u_int8_t Reserved; + u_int16_t Size; + + u_int8_t BSSID[ETHER_ADDR_LEN]; + u_int8_t SSID[32]; + u_int8_t BSSType; + u_int8_t Channel; + u_int8_t SSIDSize; + u_int8_t Res[3]; +}; + + +/* + * The At76c503 adapters come with different types of radios on them. + * At this moment the driver supports adapters with RFMD and Intersil radios. + */ + +/* The config structure of an RFMD radio */ +struct atu_rfmd_conf { + u_int8_t CR20[14]; + u_int8_t CR21[14]; + u_int8_t BB_CR[14]; + u_int8_t PidVid[4]; + u_int8_t MACAddr[ETHER_ADDR_LEN]; + u_int8_t RegulatoryDomain; + u_int8_t LowPowerValues[14]; + u_int8_t NormalPowerValues[14]; + u_int8_t Reserved[3]; + /* then we have 84 bytes, somehow Windows reads 95?? */ + u_int8_t Rest[11]; +}; + +/* The config structure of an Intersil radio */ +struct atu_intersil_conf { + u_int8_t MACAddr[ETHER_ADDR_LEN]; + /* From the HFA3861B manual : */ + /* Manual TX power control (7bit : -64 to 63) */ + u_int8_t CR31[14]; + /* TX power measurement */ + u_int8_t CR58[14]; + u_int8_t PidVid[4]; + u_int8_t RegulatoryDomain; + u_int8_t Reserved[1]; +}; + + +/* Firmware information request */ +struct atu_fw { + u_int8_t major; + u_int8_t minor; + u_int8_t patch; + u_int8_t build; +}; + + +/* + * The header the AT76c503 puts in front of RX packets (for both managment & + * data) + */ +struct at76c503_rx_buffer { + u_int16_t wlength; + u_int8_t rx_rate; + u_int8_t newbss; + u_int8_t fragmentation; + u_int8_t rssi; + u_int8_t link_quality; + u_int8_t noise_level; + u_int32_t rx_time; +}; + +/* The total packet the AT76c503 spits out looks like this */ +struct atu_rxpkt { + struct at76c503_rx_buffer AtHeader; + struct wi_80211_hdr WiHeader; + u_int8_t Packet[2312 + 4]; +}; + +/* + * The header we have to put in front of a TX packet before sending it to the + * AT76c503 + */ +struct at76c503_tx_buffer { + u_int16_t wlength; + u_int8_t tx_rate; + u_int8_t padding; + u_int8_t reserved[4]; +}; + +/* The total packet we send to the AT76c503 looks like this */ +struct atu_txpkt { + struct at76c503_tx_buffer AtHeader; + struct wi_80211_hdr WiHeader; + /* TODO - change this to a more correct value */ + u_int8_t Packet[2312]; +}; + +/* Managment packet */ +struct atu_mgmt_packet { + struct at76c503_tx_buffer athdr; + struct wi_mgmt_hdr mgmt_hdr; + + u_int8_t payload[0]; +}; + +/* Authentication packet */ +struct atu_auth_packet { + struct at76c503_tx_buffer athdr; + struct wi_mgmt_hdr mgmt_hdr; + struct wi_mgmt_auth_hdr auth_hdr; + + u_int8_t challenge[0]; +}; + +/* Association packet */ +struct atu_assoc_packet { + struct at76c503_tx_buffer athdr; + struct wi_mgmt_hdr mgmt_hdr; + u_int16_t capability; + u_int16_t listen_interval; + + u_int8_t data[0]; +}; + + +struct wi_80211_beacon { + u_int8_t timestamp[8]; + u_int16_t interval; + u_int16_t flags; + u_int8_t data[1500]; +}; + +struct tlv { + u_int8_t type; + u_int8_t length; + u_int8_t value[255]; +}; + +#define TYPE_MASK 0x000c + + + + +#define NR(x) (void *)(x) + +/* + * The linux driver uses seperate routines for every mib request they do + * (eg. set_radio / set_preamble / set_frag / etc etc ) + * We just define a list of types, sizes and offsets and use those + */ + +/* Name Type Size Index */ +#define MIB_LOCAL 0x01 +#define MIB_LOCAL__BEACON_ENABLE MIB_LOCAL, 1, 2 +#define MIB_LOCAL__AUTO_RATE_FALLBACK MIB_LOCAL, 1, 3 +#define MIB_LOCAL__SSID_SIZE MIB_LOCAL, 1, 5 +#define MIB_LOCAL__PREAMBLE MIB_LOCAL, 1, 9 +#define MIB_MAC_ADDR 0x02 +#define MIB_MAC_ADDR__ADDR MIB_MAC_ADDR, 6, 0 +#define MIB_MAC 0x03 +#define MIB_MAC__FRAG MIB_MAC, 2, 8 +#define MIB_MAC__RTS MIB_MAC, 2, 10 +#define MIB_MAC__DESIRED_SSID MIB_MAC, 32, 28 +#define MIB_MAC_MGMT 0x05 +#define MIB_MAC_MGMT__BEACON_PERIOD MIB_MAC_MGMT, 2, 0 +#define MIB_MAC_MGMT__CURRENT_BSSID MIB_MAC_MGMT, 6, 14 +#define MIB_MAC_MGMT__CURRENT_ESSID MIB_MAC_MGMT, 32, 20 +#define MIB_MAC_MGMT__POWER_MODE MIB_MAC_MGMT, 1, 53 +#define MIB_MAC_MGMT__IBSS_CHANGE MIB_MAC_MGMT, 1, 54 +#define MIB_MAC_WEP 0x06 +#define MIB_MAC_WEP__PRIVACY_INVOKED MIB_MAC_WEP, 1, 0 +#define MIB_MAC_WEP__KEY_ID MIB_MAC_WEP, 1, 1 +#define MIB_MAC_WEP__ICV_ERROR_COUNT MIB_MAC_WEP, 4, 4 +#define MIB_MAC_WEP__EXCLUDED_COUNT MIB_MAC_WEP, 4, 8 +#define MIB_MAC_WEP__KEYS(nr) MIB_MAC_WEP, 13, 12+(nr)*13 +#define MIB_MAC_WEP__ENCR_LEVEL MIB_MAC_WEP, 1, 64 +#define MIB_PHY 0x07 +#define MIB_PHY__CHANNEL MIB_PHY, 1, 20 +#define MIB_PHY__REG_DOMAIN MIB_PHY, 1, 23 +#define MIB_FW_VERSION 0x08 +#define MIB_DOMAIN 0x09 +#define MIB_DOMAIN__POWER_LEVELS MIB_DOMAIN, 14, 0 +#define MIB_DOMAIN__CHANNELS MIB_DOMAIN, 14, 14 + +#define ATU_WEP_OFF 0 +#define ATU_WEP_40BITS 1 +#define ATU_WEP_104BITS 2 + +#define POWER_MODE_ACTIVE 1 +#define POWER_MODE_SAVE 2 +#define POWER_MODE_SMART 3 + +#define PREAMBLE_SHORT 1 +#define PREAMBLE_LONG 0 |