All pastes #928603 Raw Edit

Something

public text v1 · immutable
·published 2008-03-05 04:36 UTC
rendered paste body
/*
 * Copyright (c) 2008 by  Alan Maguire <alan.maguire@sun.com>
 * Copyright (c) 2008 by  Ben Taylor <bentaylor.solx86@gmail.com>
 * Copyright (c) 2007 by  Lukas Turek <turek@ksvi.mff.cuni.cz>
 * Copyright (c) 2007 by  Jiri Svoboda <jirik.svoboda@seznam.cz>
 * Copyright (c) 2007 by  Martin Krulis <martin.krulis@matfyz.cz>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

/*-
 * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr>
 * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * ZD1211 wLAN driver
 * Device hardware control
 *
 * Control the ZD1211 chip and the RF chip.
 */

#include <sys/byteorder.h>
#include <sys/strsun.h>

#include "zyd_usb.h"
#include "zyd_reg.h"
#include "zyd_impl.h"
#include "zyd_hw.h"

void			zyd_set_channel(struct zyd_softc *, struct ieee80211_channel *);
zyd_res			zyd_al2230_rf_init(struct zyd_softc *);
zyd_res			zyd_al2230_rf_init_b(struct zyd_softc *);
zyd_res			zyd_al2230_switch_radio(struct zyd_softc *, boolean_t);
zyd_res			zyd_al2230_set_channel(struct zyd_softc *, uint8_t);
zyd_res			zyd_rfmd_rf_init(struct zyd_softc *);
zyd_res			zyd_rfmd_switch_radio(struct zyd_softc *, boolean_t);
zyd_res			zyd_rfmd_set_channel(struct zyd_softc *, uint8_t);
zyd_res			zyd_al7230b_rf_init(struct zyd_softc *);
zyd_res			zyd_al7230b_switch_radio(struct zyd_softc *, boolean_t);
zyd_res			zyd_al7230b_set_channel(struct zyd_softc *, uint8_t);
const char		*zyd_rf_name(uint8_t);

/* io write sequences to initialize RF-independent PHY registers */
static const struct zyd_iowrite16 zyd_def_phy[] = ZYD_DEF_PHY;
static const struct zyd_iowrite16 zyd_def_phyB[] = ZYD_DEF_PHYB;

/*
 * Read a 32-bit I/O register.
 *
 *	sc	soft state
 *	reg	register number
 *	*val	place to store the value
 */
zyd_res
zyd_read32(struct zyd_softc *sc, uint16_t reg, uint32_t *val)
{
	zyd_res result;
	uint16_t tmp[4];
	uint16_t regs[2];

	regs[0] = LE_16(ZYD_REG32_HI(reg));
	regs[1] = LE_16(ZYD_REG32_LO(reg));

	result = zyd_usb_ioread_req(&sc->usb, regs, sizeof (regs),
					tmp, sizeof (tmp));

	if (result != USB_SUCCESS)
		return (ZYD_FAILURE);

	if (tmp[0] != regs[0] || tmp[2] != regs[1]) {
		ZYD_WARN("ioread response doesn't match request");
		ZYD_WARN("requested regs %04x, %04x; got %04x, %04x",
			LE_16(regs[0]), LE_16(regs[1]),
			LE_16(tmp[0]), LE_16(tmp[2]));
		return (ZYD_FAILURE);
	}

	*val = ((uint32_t)LE_16(tmp[1]) << 16) |
		(uint32_t)LE_16(tmp[3]);

	return (ZYD_SUCCESS);
}

/*
 * Write a 32-bit I/O register.
 *
 *	sc	soft state
 *	reg	register number
 *	val	value to write
 */
zyd_res
zyd_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val)
{
	zyd_res result;
	uint16_t tmp[4];

	tmp[0] = LE_16(ZYD_REG32_HI(reg));
	tmp[1] = LE_16(val >> 16);
	tmp[2] = LE_16(ZYD_REG32_LO(reg));
	tmp[3] = LE_16(val & 0xffff);

	result = zyd_usb_cmd_send(&sc->usb, ZYD_CMD_IOWR, tmp, sizeof (tmp));

	return (result);
}

/*
 * Read a 16-bit I/O register.
 *
 *	sc	soft state
 *	reg	register number
 *	*val	place to store the value
 */
zyd_res
zyd_read16(struct zyd_softc *sc, uint16_t reg, uint16_t *val)
{
	zyd_res result;
	uint16_t tmp[2];
	uint16_t regbuf;

	regbuf = LE_16(reg);

	result = zyd_usb_ioread_req(&sc->usb, &regbuf, sizeof (regbuf),
		tmp, sizeof (tmp));

	if (result != USB_SUCCESS)
		return (ZYD_FAILURE);

	if (tmp[0] != regbuf) {
		ZYD_WARN("ioread response doesn't match request");
		ZYD_WARN("requested reg %04x; got %04x",
			LE_16(regbuf), LE_16(tmp[0]));
		return (ZYD_FAILURE);
	}

	if (result != USB_SUCCESS)
		return (ZYD_FAILURE);

	*val = LE_16(tmp[1]);

	return (ZYD_SUCCESS);
}

/*
 * Write a 16-bit I/O register.
 *
 *	sc	soft state
 *	reg	register number
 *	val	value to write
 */
zyd_res
zyd_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val)
{
	zyd_res result;
	uint16_t tmp[2];

	tmp[0] = LE_16(ZYD_REG32_LO(reg));
	tmp[1] = LE_16(val & 0xffff);

	result = zyd_usb_cmd_send(&sc->usb, ZYD_CMD_IOWR, tmp, sizeof (tmp));

	return (result);
}

/*
 * Write an array of 16-bit registers.
 *
 *	sc	soft state
 *	*reqa	array of register-value pairs
 *	n	number of registers
 */
zyd_res
zyd_write16a(struct zyd_softc *sc, const struct zyd_iowrite16 *reqa, int n)
{
	zyd_res res;
	int i;

	for (i=0; i<n; i++) {
		res = zyd_write16(sc, reqa[i].reg, reqa[i].value);
		if (res != ZYD_SUCCESS)
			return (ZYD_FAILURE);
	}

	return (ZYD_SUCCESS);
}

/*
 * Lock PHY registers.
 */
void
zyd_lock_phy(struct zyd_softc *sc)
{
	uint32_t tmp;

	(void)zyd_read32(sc, ZYD_MAC_MISC, &tmp);
	tmp &= ~ZYD_UNLOCK_PHY_REGS;
	(void)zyd_write32(sc, ZYD_MAC_MISC, tmp);
}

/*
 * Unlock PHY registers.
 */
void
zyd_unlock_phy(struct zyd_softc *sc)
{
	uint32_t tmp;

	(void)zyd_read32(sc, ZYD_MAC_MISC, &tmp);
	tmp |= ZYD_UNLOCK_PHY_REGS;
	(void)zyd_write32(sc, ZYD_MAC_MISC, tmp);
}

/*
 * Read MAC address from EEPROM.
 */
static zyd_res
zyd_read_mac(struct zyd_softc *sc)
{
	uint32_t tmp;

	ZYD_DEBUG("read first part of mac");
	if (zyd_read32(sc, ZYD_EEPROM_MAC_ADDR_P1, &tmp) != ZYD_SUCCESS)
		return (ZYD_FAILURE);

	sc->macaddr[0] = tmp & 0xff;
	sc->macaddr[1] = tmp >>  8;
	sc->macaddr[2] = tmp >> 16;
	sc->macaddr[3] = tmp >> 24;

	ZYD_DEBUG("read second part of mac");

	if (zyd_read32(sc, ZYD_EEPROM_MAC_ADDR_P2, &tmp) != ZYD_SUCCESS)
		return (ZYD_FAILURE);

	sc->macaddr[4] = tmp & 0xff;
	sc->macaddr[5] = tmp >>  8;

	ZYD_DEBUG("done");

	return (ZYD_SUCCESS);
}

/*
 * Write bits to RF configuration register.
 */
zyd_res
zyd_rfwrite(struct zyd_softc *sc, uint32_t val, int bits)
{
	uint16_t cr203;
	struct zyd_rfwrite req;
	uint16_t tmp;
	int bit;
	zyd_res res;
	int i;

	if (zyd_read16(sc, ZYD_CR203, &cr203) != ZYD_SUCCESS)
		return (ZYD_FAILURE);

	cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA);

	req.code = LE_16(ZYD_RFCFG_VALUE);
	req.width = LE_16((uint16_t)bits);

	for (i = 0; i < bits; i++) {
		bit = (val & (1 << (bits - i - 1))) != 0;
		tmp = LE_16(cr203) | (bit ? LE_16(ZYD_RF_DATA) : 0);
		req.bit[i] = tmp;
	}
	res = zyd_usb_cmd_send(&sc->usb, ZYD_CMD_RFCFG, &req,
				sizeof(uint16_t)*(2+bits));

	if (res != ZYD_SUCCESS) {
		ZYD_WARN("failed configuring rf register");
		return (ZYD_FAILURE);
	}

	return (ZYD_SUCCESS);
}

/*
 * Control the LEDs.
 */
static void
zyd_set_led(struct zyd_softc *sc, int which, boolean_t on)
{
	uint32_t tmp;

	(void)zyd_read32(sc, ZYD_MAC_TX_PE_CONTROL, &tmp);
	tmp &= ~which;
	if (on == B_TRUE)
		tmp |= which;
	(void)zyd_write32(sc, ZYD_MAC_TX_PE_CONTROL, tmp);
}

/*
 * Set MAC address.
 */
static void
zyd_set_macaddr(struct zyd_softc *sc, const uint8_t *addr)
{
	uint32_t tmp;

	tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0];
	(void)zyd_write32(sc, ZYD_MAC_MACADRL, tmp);

	tmp = addr[5] << 8 | addr[4];
	(void)zyd_write32(sc, ZYD_MAC_MACADRH, tmp);
}

/*
 * Read data from EEPROM.
 */
static void
zyd_read_eeprom(struct zyd_softc *sc)
{
	uint32_t tmp;
	uint16_t val;
	int i;

	/* read RF chip type */
	(void)zyd_read32(sc, ZYD_EEPROM_POD, &tmp);
	sc->rf_rev = tmp & 0x0f;
	sc->pa_rev = (tmp >> 16) & 0x0f;
	sc->fix_cr47 = (tmp >> 8) & 0x01;
	sc->fix_cr157 = (tmp >> 13) & 0x01;
	ZYD_DEBUG("sc->fix_cr47 = 0x%x", sc->fix_cr47);
	ZYD_DEBUG("sc->fix_cr157 = 0x%x", sc->fix_cr157);
	ZYD_DEBUG("Found RF %s chip, sc->rf_rev = 0x%x\n", zyd_rf_name(sc->rf_rev), sc->rf_rev);

	/* read regulatory domain (currently unused) */
	(void)zyd_read32(sc, ZYD_EEPROM_SUBID, &tmp);
	sc->regdomain = tmp >> 16;
	ZYD_DEBUG("regulatory domain %x\n", sc->regdomain);

	/* read Tx power calibration tables */
	for (i = 0; i < 7; i++) {
		(void)zyd_read16(sc, ZYD_EEPROM_PWR_CAL + i, &val);
		sc->pwr_cal[i * 2] = val >> 8;
		sc->pwr_cal[i * 2 + 1] = val & 0xff;

		(void)zyd_read16(sc, ZYD_EEPROM_PWR_INT + i, &val);
		sc->pwr_int[i * 2] = val >> 8;
		sc->pwr_int[i * 2 + 1] = val & 0xff;

		(void)zyd_read16(sc, ZYD_EEPROM_36M_CAL + i, &val);
		sc->ofdm36_cal[i * 2] = val >> 8;
		sc->ofdm36_cal[i * 2 + 1] = val & 0xff;

		(void)zyd_read16(sc, ZYD_EEPROM_48M_CAL + i, &val);
		sc->ofdm48_cal[i * 2] = val >> 8;
		sc->ofdm48_cal[i * 2 + 1] = val & 0xff;

		(void)zyd_read16(sc, ZYD_EEPROM_54M_CAL + i, &val);
		sc->ofdm54_cal[i * 2] = val >> 8;
		sc->ofdm54_cal[i * 2 + 1] = val & 0xff;
	}
}

/*
 * Finish ZD chip initialization.
 */
static zyd_res
zyd_hw_configure(struct zyd_softc *sc)
{
	zyd_res res;
	uint32_t tmp;

	/* specify that the plug and play is finished */
	(void)zyd_write32(sc, ZYD_MAC_AFTER_PNP, 1);

	(void)zyd_read16(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->fwbase);
	ZYD_DEBUG("firmware base address=0x%04x\n", sc->fwbase);

	/* retrieve firmware revision number */
	(void)zyd_read16(sc, sc->fwbase + ZYD_FW_FIRMWARE_REV, &sc->fw_rev);
	ZYD_DEBUG("firmware revision number=0x%04x\n", sc->fw_rev);

	(void)zyd_write32(sc, ZYD_CR_GPI_EN, 0);
	(void)zyd_write32(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f);

	/* disable interrupts */
	(void)zyd_write32(sc, ZYD_CR_INTERRUPT, 0);

	/* Init RF chip-independent PHY registers */
	zyd_lock_phy(sc);
	if (sc->mac_rev == ZYD_ZD1211)
	{
		res = zyd_write16a(sc, zyd_def_phy,
			ZYD_ARRAY_LENGTH(zyd_def_phy));
	}
	else
	{
		res = zyd_write16a(sc, zyd_def_phyB,
			ZYD_ARRAY_LENGTH(zyd_def_phyB));
	}
	if (sc->fix_cr157) {
		if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0)
			(void)zyd_write32(sc, ZYD_CR157, tmp >> 8);
	}
	zyd_unlock_phy(sc);

	if (res != ZYD_SUCCESS)
		return (ZYD_FAILURE);

	/* HMAC initialization magic */
	if (sc->mac_rev == ZYD_ZD1211)
	{
		zyd_write32(sc, ZYD_MAC_RETRY, 0x00000002);
	}
	else
	{
		zyd_write32(sc, ZYD_MACB_MAX_RETRY, 0x02020202);
		zyd_write32(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f);
		zyd_write32(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f);
		zyd_write32(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f);
		zyd_write32(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f);
		zyd_write32(sc, ZYD_MACB_AIFS_CTL1, 0x00280028);
		zyd_write32(sc, ZYD_MACB_AIFS_CTL2, 0x008C003c);
		zyd_write32(sc, ZYD_MACB_TXOP, 0x01800824);
	}
	zyd_write32(sc, ZYD_MAC_ACK_EXT, 0x00000020);
	zyd_write32(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808);
	zyd_write32(sc, ZYD_MAC_SNIFFER, 0x00000000);
	zyd_write32(sc, ZYD_MAC_RXFILTER, 0x00000000);
	zyd_write32(sc, ZYD_MAC_GHTBL, 0x00000000);
	zyd_write32(sc, ZYD_MAC_GHTBH, 0x80000000);
	zyd_write32(sc, ZYD_MAC_MISC, 0x000000a4);
	zyd_write32(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f);
	zyd_write32(sc, ZYD_MAC_BCNCFG, 0x00f00401);
	zyd_write32(sc, ZYD_MAC_PHY_DELAY2, 0x00000000);
	zyd_write32(sc, ZYD_MAC_ACK_EXT, 0x00000080);
	zyd_write32(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000);
	zyd_write32(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100);
	zyd_write32(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0547c032);
	zyd_write32(sc, ZYD_CR_RX_PE_DELAY, 0x00000070);
	zyd_write32(sc, ZYD_CR_PS_CTRL, 0x10000000);
	zyd_write32(sc, ZYD_MAC_RTSCTSRATE, 0x02030203);
	zyd_write32(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640);
	zyd_write32(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114);

	return (ZYD_SUCCESS);
}

/*
 * Set active channel number.
 */
void
zyd_hw_set_channel(struct zyd_softc *sc, uint8_t chan)
{
	uint32_t tmp;

	zyd_lock_phy(sc);

	switch(sc->rf_rev) {
		case ZYD_RF_AL2230:
			zyd_al2230_set_channel(sc, chan);
			break;
		case ZYD_RF_RFMD:
			zyd_rfmd_set_channel(sc, chan);
			break;
		case ZYD_RF_AL7230B:
			zyd_al7230b_set_channel(sc, chan);
			break;
	}

	/* update Tx power */
	(void)zyd_write16(sc, ZYD_CR31, sc->pwr_int[chan - 1]);
	if (sc->mac_rev == ZYD_ZD1211B) {
		(void)zyd_write16(sc, ZYD_CR67, sc->ofdm36_cal[chan - 1]);
		(void)zyd_write16(sc, ZYD_CR66, sc->ofdm48_cal[chan - 1]);
		(void)zyd_write16(sc, ZYD_CR65, sc->ofdm54_cal[chan - 1]);

		(void)zyd_write16(sc, ZYD_CR68, sc->pwr_cal[chan - 1]);

		(void)zyd_write16(sc, ZYD_CR69, 0x28);
		(void)zyd_write16(sc, ZYD_CR69, 0x2a);
	}

	if (sc->fix_cr47) {
		/* set CCK baseband gain from EEPROM */
		if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0)
			(void)zyd_write16(sc, ZYD_CR47, tmp & 0xff);
	}

	(void)zyd_write32(sc, ZYD_CR_CONFIG_PHILIPS, 0);

	zyd_unlock_phy(sc);
}

/*
 * Initialize hardware.
 */
zyd_res
zyd_hw_init(struct zyd_softc *sc)
{
	zyd_res res;

	if (!sc->fwloaded && zyd_usb_init(sc) != ZYD_SUCCESS)
		return (ZYD_FAILURE);

	if (zyd_read_mac(sc) != ZYD_SUCCESS) {
		ZYD_WARN("failed to read MAC address");
		return (ZYD_FAILURE);
	}

	zyd_read_eeprom(sc);
	switch (sc->rf_rev) {
		case ZYD_RF_AL2230:
		case ZYD_RF_RFMD:
		case ZYD_RF_AL7230B:
			break;
		default:
			ZYD_WARN("Unsupported RF %s, chip type 0x%x",
				zyd_rf_name(sc->rf_rev), sc->rf_rev);
			return (ZYD_FAILURE);
			break;
	}

	if (zyd_hw_configure(sc) != ZYD_SUCCESS) {
		ZYD_WARN("Failed to configure hardware.");
		return (ZYD_FAILURE);
	}

	/* RF chip init */
	zyd_lock_phy(sc);
	switch(sc->rf_rev) {
		case ZYD_RF_AL2230:
			if (sc->mac_rev == ZYD_ZD1211) {
				res = zyd_al2230_rf_init(sc);
			} else {
				res = zyd_al2230_rf_init_b(sc);
			}
			break;
		case ZYD_RF_RFMD:
			res = zyd_rfmd_rf_init(sc);
			break;
		case ZYD_RF_AL7230B:
			res = zyd_al7230b_rf_init(sc);
			break;
		default:
			ZYD_WARN("Unsupported Radio %s, code = 0x%x",
				zyd_rf_name(sc->rf_rev), sc->rf_rev);
			res = ZYD_FAILURE;
			break;
	}
	zyd_unlock_phy(sc);

	if (res != ZYD_SUCCESS) {
		ZYD_WARN("Failed to configure RF chip");
		return (ZYD_FAILURE);
	}

	ZYD_DEBUG("Zyd HW inited. MAC=%02X:%02X:%02X:%02X:%02X:%02X",
		sc->macaddr[0],sc->macaddr[1],sc->macaddr[2],
		sc->macaddr[3],sc->macaddr[4],sc->macaddr[5]);

	return (ZYD_SUCCESS);
}

#if 0
/* Initialize the radios */
zyd_res
zyd_rf_attach(struct zyd_softc *sc, uint8_t type)
{
	struct zyd_rf *rf = &sc->sc_rf;

	rf->rf_sc = sc;

	switch (type) {
		case ZYD_RF_AL2230:
			if (sc->mac_rev == ZYD_ZD1211)
				rf->init         = zyd_al2230_rf_init;
			else
				rf->init         = zyd_al2230_rf_init_b;
			rf->switch_radio = zyd_al2230_switch_radio;
			rf->set_channel  = zyd_al2230_set_channel;
			rf->width        = ZYD_AL2230_RF_BITS;	/* 24-bit RF values */
			break;
		default:
			printf("%s: sorry, radio \"%s\" is not supported yet\n",
			    "Changeme", zyd_rf_name(type));
			return (ZYD_FAILURE);
	}
	return (ZYD_SUCCESS);
}
#endif

/*
 * Deinitialize hardware.
 */
void
zyd_hw_deinit(struct zyd_softc *sc)
{
	zyd_usb_deinit(&sc->usb);
}

/*
 * Activate the device.
 */
zyd_res
zyd_hw_start(struct zyd_softc *sc)
{
	zyd_res res;
	struct ieee80211com *ic = &sc->ic;

	if (zyd_usb_data_in_enable(&sc->usb) != ZYD_SUCCESS) {
		ZYD_WARN("error starting rx transfer");
		return (ZYD_FAILURE);
	}

	ZYD_DEBUG("setting MAC address\n");
	zyd_set_macaddr(sc, sc->macaddr);

	/* we'll do software WEP decryption for now */
	ZYD_DEBUG("setting encryption type\n");
	res = zyd_write32(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER);
	if (res != ZYD_SUCCESS)
		return (ZYD_FAILURE);

	/* promiscuous mode */
	(void)zyd_write32(sc, ZYD_MAC_SNIFFER, 0);

	/* try to catch all packets */
	(void)zyd_write32(sc, ZYD_MAC_RXFILTER, ZYD_FILTER_BSS);

	/* switch radio transmitter ON */
	switch(sc->rf_rev) {
		case ZYD_RF_AL2230:
			zyd_al2230_switch_radio(sc, B_TRUE);
			break;
		case ZYD_RF_RFMD:
			zyd_rfmd_switch_radio(sc, B_TRUE);
			break;
		case ZYD_RF_AL7230B:
			zyd_al7230b_switch_radio(sc, B_TRUE);
			break;
	}

	/* set basic rates */
	ZYD_DEBUG("set basic rates\n");
	if (ic->ic_curmode == IEEE80211_MODE_11B)
		(void)zyd_write32(sc, ZYD_MAC_BAS_RATE, 0x0003);
	else if (ic->ic_curmode == IEEE80211_MODE_11A)
		(void)zyd_write32(sc, ZYD_MAC_BAS_RATE, 0x1500);
	else	/* assumes 802.11b/g */
		(void)zyd_write32(sc, ZYD_MAC_BAS_RATE, 0x000f);

	/* set mandatory rates */
	ZYD_DEBUG("set mandatory rates\n");
	if (ic->ic_curmode == IEEE80211_MODE_11B)
		(void)zyd_write32(sc, ZYD_MAC_MAN_RATE, 0x000f);
	else if (ic->ic_curmode == IEEE80211_MODE_11A)
		(void)zyd_write32(sc, ZYD_MAC_MAN_RATE, 0x1500);
	else	/* assumes 802.11b/g */
		(void)zyd_write32(sc, ZYD_MAC_MAN_RATE, 0x150f);

	/* enable interrupts */
	(void)zyd_write32(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK);

	zyd_set_led(sc, ZYD_LED2, B_TRUE);

	return (ZYD_SUCCESS);
}

/*
 * Deactivate the device.
 */
void
zyd_hw_stop(struct zyd_softc *sc)
{
	ZYD_DEBUG("begin\n");

	/* switch radio transmitter OFF */
	switch(sc->rf_rev) {
		case ZYD_RF_AL2230:
			zyd_al2230_switch_radio(sc, B_FALSE);
			break;
		case ZYD_RF_RFMD:
			zyd_rfmd_switch_radio(sc, B_FALSE);
			break;
		case ZYD_RF_AL7230B:
			zyd_al7230b_switch_radio(sc, B_FALSE);
			break;
	}

	/* disable reception */
	(void)zyd_write32(sc, ZYD_MAC_RXFILTER, 0);

	/* disable interrupts */
	(void)zyd_write32(sc, ZYD_CR_INTERRUPT, 0);

	zyd_usb_data_in_disable(&sc->usb);
	zyd_set_led(sc, ZYD_LED2, B_FALSE);
}

uint8_t
zyd_plcp_signal(uint16_t rate)
{
	switch (rate) {
	/* CCK rates (returned values are device-dependent) */
	case 2:		return 0x0;
	case 4:		return 0x1;
	case 11:	return 0x2;
	case 22:	return 0x3;

	/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
	case 12:	return 0xb;
	case 18:	return 0xf;
	case 24:	return 0xa;
	case 36:	return 0xe;
	case 48:	return 0x9;
	case 72:	return 0xd;
	case 96:	return 0x8;
	case 108:	return 0xc;

	/* unsupported rates (should not get there) */
	default:	return 0xff;
	}
}

/*
 * Transmit a 802.11 frame.
 *
 * Constructs a packet from zyd_tx_header and 802.11 frame data
 * and sends it to the chip.
 */
zyd_res
zyd_hw_send_frame(ieee80211com_t *ic, struct zyd_softc *sc, mblk_t *mp)
{
	void *buffer;
	uint8_t *buf_data;
	struct zyd_tx_header *buf_hdr;
	struct ieee80211_frame *wh;
	struct ieee80211_node *in;
	struct ieee80211_key *k;
	mblk_t *m, *m0;
	int len, total_len, off, mblen;
	uint16_t frame_size, additional_size, rate;
	uint8_t service;

	m = allocb(msgdsize(mp) + 32, BPRI_MED);
	if (m == NULL) {
		sc->tx_nobuf++;
		return (ZYD_FAILURE);
	}

	for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) {
		mblen = MBLKL(m0);
		(void) memcpy(m->b_rptr + off, m0->b_rptr, mblen);
		off += mblen;
	}
	m->b_wptr += off;

	wh = (struct ieee80211_frame *)m->b_rptr;
	in = ieee80211_find_txnode(ic, wh->i_addr1);

	if (in == NULL) {
		freemsg(m);
		sc->tx_err++;
		return (ZYD_FAILURE);
	}
	in->in_inact = 0;

	(void) ieee80211_encap(ic, m, in);

	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
		k = ieee80211_crypto_encap(ic, m);
		if (k == NULL) {
			sc->tx_err++;
			ieee80211_free_node(in);
			freemsg(m);
			return (ZYD_FAILURE);
		}
		/* packet header may have moved, reset our local pointer */
		wh = (struct ieee80211_frame *)m->b_rptr;
	}

	/*
	 * pickup a rate. May need work to make adaptive - at present,
	 * picks best rate for mode.
	 */
	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
	    IEEE80211_FC0_TYPE_MGT) {
		/* mgmt frames are sent at 1M */
		rate = (uint16_t)in->in_rates.ir_rates[0];
	} else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
		rate = (uint16_t)ic->ic_sup_rates[ic->ic_curmode].
		    ir_rates[ic->ic_fixed_rate];
	} else {
		in->in_txrate = in->in_rates.ir_nrates - 1;
		rate = (uint16_t)ic->ic_sup_rates[ic->ic_curmode].
		    ir_rates[in->in_txrate];
	}
	rate &= IEEE80211_RATE_VAL;
	if (rate == 0)	/* XXX should not happen */
		rate = 2;

	/* Get total length of frame */
	len = msgsize(m);

	/* Total number of bytes to send to the chip */
	total_len = sizeof (struct zyd_tx_header) + len;

	/* Allocate a buffer to store the packet */
	buffer = kmem_alloc(total_len, KM_NOSLEEP);
	if (buffer == NULL) {
		ieee80211_free_node(in);
		freemsg(m);
		return (ZYD_FAILURE);
	}

	/* Get pointers to header buffer and 802.11 frame buffer */
	buf_hdr = (struct zyd_tx_header *)buffer;
	buf_data = ((uint8_t *)buffer) + sizeof (struct zyd_tx_header);

	/*** Fill header information ***/

	/*
	 * Frame size in bytes including CRC32 checksum
	 */
	frame_size = (uint16_t)len + IEEE80211_FCS_LEN;
	buf_hdr->frame_size = LE_16(frame_size);

	/*
	 * Compute "packet size". What the 10 stands for,
	 * nobody knows.
	 */
	additional_size = sizeof (struct zyd_tx_header) + 10;
	if (sc->mac_rev == ZYD_ZD1211)
		buf_hdr->packet_size = LE_16(frame_size + additional_size);
	else
		buf_hdr->packet_size = LE_16(additional_size);

	buf_hdr->rate_mod_flags = LE_8(zyd_plcp_signal(rate));
	buf_hdr->type_flags = LE_8(ZYD_TX_FLAG_BACKOFF);
	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
		/* multicast frames are not sent at OFDM rates in 802.11b/g */
		if (frame_size > ic->ic_rtsthreshold) {
			buf_hdr->type_flags |= ZYD_TX_FLAG_RTS;
		} else if (ZYD_RATE_IS_OFDM(rate) &&
		    (ic->ic_flags & IEEE80211_F_USEPROT)) {
			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
				buf_hdr->type_flags |= ZYD_TX_FLAG_CTS_TO_SELF;
			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
				buf_hdr->type_flags |= ZYD_TX_FLAG_RTS;
		}
	} else
		buf_hdr->type_flags |= ZYD_TX_FLAG_MULTICAST;

	if ((wh->i_fc[0] &
	    (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
	    (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL))
		buf_hdr->type_flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);

	if (ZYD_RATE_IS_OFDM(rate)) {
		buf_hdr->rate_mod_flags |= ZYD_TX_RMF_OFDM;
		if (ic->ic_curmode == IEEE80211_MODE_11A)
			buf_hdr->rate_mod_flags |= ZYD_TX_RMF_5GHZ;
	} else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
		buf_hdr->rate_mod_flags |= ZYD_TX_RMF_SH_PREAMBLE;

	/*
	 * Compute frame duration and length-extension service flag.
	 */
	service = 0x00;

	buf_hdr->frame_duration = LE_16((16 * frame_size + rate - 1) / rate);
	buf_hdr->service = LE_8(service);
	buf_hdr->next_frame_duration = LE_16(0);

	if (rate == 22) {
		const int remainder = (16 * frame_size) % 22;
		if (remainder != 0 && remainder < 7)
			buf_hdr->service |= ZYD_TX_SERVICE_LENGTH_EXTENSION;
	}

	/* Copy the 802.11 frame */
	mcopymsg(m, buf_data);

	/* Send it to the device */
	zyd_usb_send_packet(&sc->usb, buffer, total_len);

	ic->ic_stats.is_tx_frags++;
	ic->ic_stats.is_tx_bytes += buf_hdr->packet_size;
	/* Free memory */
	ieee80211_free_node(in);
	kmem_free(buffer, total_len);
	buffer = NULL;
	buf_hdr = NULL;
	buf_data = NULL;

	return (ZYD_SUCCESS);
}

const char *
zyd_rf_name(uint8_t type)
{
	static const char * const zyd_rfs[] = {
		"unknown", "unknown", "UW2451",   "UCHIP",     "AL2230",
		"AL7230B", "THETA",   "AL2210",   "MAXIM_NEW", "GCT",
		"PV2000",  "RALINK",  "INTERSIL", "RFMD",      "MAXIM_NEW2",
		"PHILIPS"
	};
	return zyd_rfs[(type > 15) ? 0 : type];
}

zyd_mac_rev_t
zyd_hw_lookup(uint16_t vendor, uint16_t product)
{
	ZYD_DEBUG("looking up chip type (ZD1211/ZD1211B) for usb%x,%x",
		vendor, product);
	return (zyd_usb_mac_rev(vendor, product));
}