code:
/*	$OpenBSD: acx111.c,v 1.22 2014/12/22 02:28:51 tedu Exp $ */

/*
 * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
 *
 * 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 The DragonFly Project.  All rights reserved.
 *
 * This code is derived from software contributed to The DragonFly Project
 * by Sepherosa Ziehau <sepherosa@gmail.com>
 *
 * 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. Neither the name of The DragonFly Project nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
 */

#include "bpfilter.h"

#include <sys/param.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/device.h>

#include <machine/bus.h>

#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_media.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>

#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_amrr.h>
#include <net80211/ieee80211_radiotap.h>

#include <dev/pci/pcireg.h>

#include <dev/ic/acxvar.h>
#include <dev/ic/acxreg.h>

#define ACX111_CONF_MEM		0x0003
#define ACX111_CONF_MEMINFO	0x0005

#define ACX111_INTR_ENABLE	(ACXRV_INTR_TX_FINI | ACXRV_INTR_RX_FINI)
/*
 * XXX do we really care about fowlling interrupts?
 *
 * ACXRV_INTR_IV_ICV_FAILURE | ACXRV_INTR_INFO |
 * ACXRV_INTR_SCAN_FINI | ACXRV_INTR_FCS_THRESHOLD
 */

#define ACX111_INTR_DISABLE	(uint16_t)~(ACXRV_INTR_CMD_FINI)

#define ACX111_RATE_2		0x0001
#define ACX111_RATE_4		0x0002
#define ACX111_RATE_11		0x0004
#define ACX111_RATE_12		0x0008
#define ACX111_RATE_18		0x0010
#define ACX111_RATE_22		0x0020
#define ACX111_RATE_24		0x0040
#define ACX111_RATE_36		0x0080
#define ACX111_RATE_44		0x0100
#define ACX111_RATE_48		0x0200
#define ACX111_RATE_72		0x0400
#define ACX111_RATE_96		0x0800
#define ACX111_RATE_108		0x1000

/* XXX skip ACX111_RATE_44 */
#define ACX111_RATE_ALL		0x1eff

#define ACX111_TXPOWER		15
#define ACX111_GPIO_POWER_LED	0x0040
#define ACX111_EE_EADDR_OFS	0x21

#define ACX111_FW_TXDESC_SIZE	(sizeof(struct acx_fw_txdesc) + 4)

#if ACX111_TXPOWER <= 12
#define ACX111_TXPOWER_VAL	1
#else
#define ACX111_TXPOWER_VAL	2
#endif

int	acx111_init(struct acx_softc *);
int	acx111_init_memory(struct acx_softc *);
void	acx111_init_fw_txring(struct acx_softc *, uint32_t);
int	acx111_write_config(struct acx_softc *, struct acx_config *);
void	acx111_set_fw_txdesc_rate(struct acx_softc *,
	    struct acx_txbuf *, int);
void	acx111_set_bss_join_param(struct acx_softc *, void *, int);

/*
 * NOTE:
 * Following structs' fields are little endian
 */
struct acx111_bss_join {
	uint16_t	basic_rates;
	uint8_t		dtim_intvl;
} __packed;

struct acx111_conf_mem {
	struct acx_conf	confcom;

	uint16_t	sta_max;	/* max num of sta, ACX111_STA_MAX */
	uint16_t	memblk_size;	/* mem block size */
	uint8_t		rx_memblk_perc;	/* percent of RX mem block, unit: 5% */
	uint8_t		fw_rxring_num;	/* num of RX ring */
	uint8_t		fw_txring_num;	/* num of TX ring */
	uint8_t		opt;		/* see ACX111_MEMOPT_ */
	uint8_t		xfer_perc;	/* frag/xfer proportion, unit: 5% */
	uint16_t	reserved0;
	uint8_t		reserved1;

	uint8_t		fw_rxdesc_num;	/* num of fw rx desc */
	uint8_t		fw_rxring_reserved1;
	uint8_t		fw_rxring_type;	/* see ACX111_RXRING_TYPE_ */
	uint8_t		fw_rxring_prio;	/* see ACX111_RXRING_PRIO_ */

	uint32_t	h_rxring_paddr; /* host rx desc start phyaddr */

	uint8_t		fw_txdesc_num;	/* num of fw tx desc */
	uint8_t		fw_txring_reserved1;
	uint8_t		fw_txring_reserved2;
	uint8_t		fw_txring_attr;	/* see ACX111_TXRING_ATTR_ */
} __packed;

#define ACX111_STA_MAX			32
#define ACX111_RX_MEMBLK_PERCENT	10	/* 50% */
#define ACX111_XFER_PERCENT		15	/* 75% */
#define ACX111_RXRING_TYPE_DEFAULT	7
#define ACX111_RXRING_PRIO_DEFAULT	0
#define ACX111_TXRING_ATTR_DEFAULT	0
#define ACX111_MEMOPT_DEFAULT		0

struct acx111_conf_meminfo {
	struct acx_conf	confcom;
	uint32_t	tx_memblk_addr;	/* start addr of tx mem blocks */
	uint32_t	rx_memblk_addr;	/* start addr of rx mem blocks */
	uint32_t	fw_rxring_start; /* start phyaddr of fw rx ring */
	uint32_t	reserved0;
	uint32_t	fw_txring_start; /* start phyaddr of fw tx ring */
	uint8_t		fw_txring_attr;	/* XXX see ACX111_TXRING_ATTR_ */
	uint16_t	reserved1;
	uint8_t		reserved2;
} __packed;

struct acx111_conf_txpower {
	struct acx_conf	confcom;
	uint8_t		txpower;
} __packed;

struct acx111_conf_option {
	struct acx_conf	confcom;
	uint32_t	feature;
	uint32_t	dataflow;	/* see ACX111_DF_ */
} __packed;

#define ACX111_DF_NO_RXDECRYPT	0x00000080
#define ACX111_DF_NO_TXENCRYPT	0x00000001

struct acx111_wepkey {
	uint8_t		mac_addr[IEEE80211_ADDR_LEN];
	uint16_t	action;		/* see ACX111_WEPKEY_ACT_ */
	uint16_t	reserved;
	uint8_t		key_len;
	uint8_t		key_type;	/* see ACX111_WEPKEY_TYPE_ */
	uint8_t		index;		/* XXX ?? */
	uint8_t		key_idx;
	uint8_t		counter[6];
#define ACX111_WEPKEY_LEN	32
	uint8_t		key[ACX111_WEPKEY_LEN];
} __packed;

#define ACX111_WEPKEY_ACT_ADD		1
#define ACX111_WEPKEY_TYPE_DEFAULT	0

static const uint16_t acx111_reg[ACXREG_MAX] = {
	ACXREG(SOFT_RESET,		0x0000),

	ACXREG(FWMEM_ADDR,		0x0014),
	ACXREG(FWMEM_DATA,		0x0018),
	ACXREG(FWMEM_CTRL,		0x001c),
	ACXREG(FWMEM_START,		0x0020),

	ACXREG(EVENT_MASK,		0x0034),

	ACXREG(INTR_TRIG,		0x00b4),
	ACXREG(INTR_MASK,		0x00d4),
	ACXREG(INTR_STATUS,		0x00f0),
	ACXREG(INTR_STATUS_CLR,		0x00e4),
	ACXREG(INTR_ACK,		0x00e8),

	ACXREG(HINTR_TRIG,		0x00ec),
	ACXREG(RADIO_ENABLE,		0x01d0),

	ACXREG(EEPROM_INIT,		0x0100),
	ACXREG(EEPROM_CTRL,		0x0338),
	ACXREG(EEPROM_ADDR,		0x033c),
	ACXREG(EEPROM_DATA,		0x0340),
	ACXREG(EEPROM_CONF,		0x0344),
	ACXREG(EEPROM_INFO,		0x0390),

	ACXREG(PHY_ADDR,		0x0350),
	ACXREG(PHY_DATA,		0x0354),
	ACXREG(PHY_CTRL,		0x0358),

	ACXREG(GPIO_OUT_ENABLE,		0x0374),
	ACXREG(GPIO_OUT,		0x037c),

	ACXREG(CMD_REG_OFFSET,		0x0388),
	ACXREG(INFO_REG_OFFSET,		0x038c),

	ACXREG(RESET_SENSE,		0x0104),
	ACXREG(ECPU_CTRL,		0x0108)
};

/* XXX */
static uint16_t	acx111_rate_map[109] = {
	ACX111_RATE_2,
	ACX111_RATE_4,
	ACX111_RATE_11,
	ACX111_RATE_22,
	ACX111_RATE_12,
	ACX111_RATE_18,
	ACX111_RATE_24,
	ACX111_RATE_36,
	ACX111_RATE_48,
	ACX111_RATE_72,
	ACX111_RATE_96,
	ACX111_RATE_108
};

void
acx111_set_param(struct acx_softc *sc)
{
	sc->chip_mem1_rid = PCIR_BAR(0);
	sc->chip_mem2_rid = PCIR_BAR(1);
	sc->chip_ioreg = acx111_reg;
	sc->chip_intr_enable = ACX111_INTR_ENABLE;
#ifndef IEEE80211_STA_ONLY
	sc->chip_intr_enable |= ACXRV_INTR_DTIM;
#endif
	sc->chip_intr_disable = ACX111_INTR_DISABLE;
	sc->chip_gpio_pled = ACX111_GPIO_POWER_LED;
	sc->chip_ee_eaddr_ofs = ACX111_EE_EADDR_OFS;

	sc->chip_phymode = IEEE80211_MODE_11G;
	sc->chip_chan_flags = IEEE80211_CHAN_CCK |
	    IEEE80211_CHAN_OFDM |
	    IEEE80211_CHAN_DYN |
	    IEEE80211_CHAN_2GHZ;
	sc->sc_ic.ic_caps = IEEE80211_C_WEP | IEEE80211_C_SHSLOT;
	sc->sc_ic.ic_phytype = IEEE80211_T_OFDM;
	sc->sc_ic.ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
	sc->sc_ic.ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;

	sc->chip_init = acx111_init;
	sc->chip_write_config = acx111_write_config;
	sc->chip_set_fw_txdesc_rate = acx111_set_fw_txdesc_rate;
	sc->chip_set_bss_join_param = acx111_set_bss_join_param;
	sc->sc_flags |= ACX_FLAG_ACX111;
}

int
acx111_init(struct acx_softc *sc)
{
	struct ifnet *ifp = &sc->sc_ic.ic_if;

	/*
	 * NOTE:
	 * Order of initialization:
	 * 1) Templates
	 * 2) Hardware memory
	 * Above order is critical to get a correct memory map
	 */
	if (acx_init_tmplt_ordered(sc) != 0) {
		printf("%s: %s can't initialize templates\n",
		    ifp->if_xname, __func__);
		return (ENXIO);
	}

	if (acx111_init_memory(sc) != 0) {
		printf("%s: %s can't initialize hw memory\n",
		    ifp->if_xname, __func__);
		return (ENXIO);
	}

	return (0);
}

int
acx111_init_memory(struct acx_softc *sc)
{
	struct acx111_conf_mem mem;
	struct acx111_conf_meminfo mem_info;
	struct ifnet *ifp = &sc->sc_ic.ic_if;

	/* Set memory configuration */
	bzero(&mem, sizeof(mem));

	mem.sta_max = htole16(ACX111_STA_MAX);
	mem.memblk_size = htole16(ACX_MEMBLOCK_SIZE);
	mem.rx_memblk_perc = ACX111_RX_MEMBLK_PERCENT;
	mem.opt = ACX111_MEMOPT_DEFAULT;
	mem.xfer_perc = ACX111_XFER_PERCENT;

	mem.fw_rxring_num = 1;
	mem.fw_rxring_type = ACX111_RXRING_TYPE_DEFAULT;
	mem.fw_rxring_prio = ACX111_RXRING_PRIO_DEFAULT;
	mem.fw_rxdesc_num = ACX_RX_DESC_CNT;
	mem.h_rxring_paddr = htole32(sc->sc_ring_data.rx_ring_paddr);

	mem.fw_txring_num = 1;
	mem.fw_txring_attr = ACX111_TXRING_ATTR_DEFAULT;
	mem.fw_txdesc_num = ACX_TX_DESC_CNT;

	if (acx_set_conf(sc, ACX111_CONF_MEM, &mem, sizeof(mem)) != 0) {
		printf("%s: can't set mem\n", ifp->if_xname);
		return (1);
	}

	/* Get memory configuration */
	if (acx_get_conf(sc, ACX111_CONF_MEMINFO, &mem_info,
	    sizeof(mem_info)) != 0) {
		printf("%s: can't get meminfo\n", ifp->if_xname);
		return (1);
	}

	/* Setup firmware TX descriptor ring */
	acx111_init_fw_txring(sc, letoh32(mem_info.fw_txring_start));

	/*
	 * There is no need to setup firmware RX descriptor ring,
	 * it is automaticly setup by hardware.
	 */

	return (0);
}

void
acx111_init_fw_txring(struct acx_softc *sc, uint32_t fw_txdesc_start)
{
	struct acx_txbuf *tx_buf;
	uint32_t desc_paddr;
	int i;

	tx_buf = sc->sc_buf_data.tx_buf;
	desc_paddr = sc->sc_ring_data.tx_ring_paddr;

	for (i = 0; i < ACX_TX_DESC_CNT; ++i) {
		tx_buf[i].tb_fwdesc_ofs = fw_txdesc_start +
		    (i * ACX111_FW_TXDESC_SIZE);

		/*
		 * Except for the following fields, rest of the fields
		 * are setup by hardware.
		 */
		FW_TXDESC_SETFIELD_4(sc, &tx_buf[i], f_tx_host_desc,
		    desc_paddr);
		FW_TXDESC_SETFIELD_1(sc, &tx_buf[i], f_tx_ctrl,
		    DESC_CTRL_HOSTOWN);

		desc_paddr += (2 * sizeof(struct acx_host_desc));
	}
}

int
acx111_write_config(struct acx_softc *sc, struct acx_config *conf)
{
	struct acx111_conf_txpower tx_power;
	struct acx111_conf_option opt;
	struct ifnet *ifp = &sc->sc_ic.ic_if;
	uint32_t dataflow;

	/* Set TX power */
	tx_power.txpower = ACX111_TXPOWER_VAL;
	if (acx_set_conf(sc, ACX_CONF_TXPOWER, &tx_power,
	    sizeof(tx_power)) != 0) {
		printf("%s: %s can't set TX power\n",
		    ifp->if_xname, __func__);
		return (ENXIO);
	}

	/*
	 * Turn off hardware WEP
	 */
	if (acx_get_conf(sc, ACX_CONF_OPTION, &opt, sizeof(opt)) != 0) {
		printf("%s: %s can't get option\n", ifp->if_xname, __func__);
		return (ENXIO);
	}

	dataflow = letoh32(opt.dataflow) |
	    ACX111_DF_NO_TXENCRYPT |
	    ACX111_DF_NO_RXDECRYPT;
	opt.dataflow = htole32(dataflow);

	if (acx_set_conf(sc, ACX_CONF_OPTION, &opt, sizeof(opt)) != 0) {
		printf("%s: %s can't set option\n", ifp->if_xname, __func__);
		return (ENXIO);
	}

	return (0);
}

void
acx111_set_fw_txdesc_rate(struct acx_softc *sc, struct acx_txbuf *tx_buf,
    int rate0)
{
	uint16_t rate;

	rate = acx111_rate_map[rate0];
	if (rate == 0)
		/* set rate to 1Mbit/s if rate was zero */
		rate = acx111_rate_map[2];

	FW_TXDESC_SETFIELD_2(sc, tx_buf, u.r2.rate111, rate);
}

void
acx111_set_bss_join_param(struct acx_softc *sc, void *param, int dtim_intvl)
{
	struct acx111_bss_join *bj = param;

	bj->basic_rates = htole16(ACX111_RATE_ALL);
	bj->dtim_intvl = dtim_intvl;
}


code:
/*	$OpenBSD: acxreg.h,v 1.12 2008/06/01 10:08:35 brad Exp $ */

/*
 * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
 *
 * 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 The DragonFly Project.  All rights reserved.
 *
 * This code is derived from software contributed to The DragonFly Project
 * by Sepherosa Ziehau <sepherosa@gmail.com>
 *
 * 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. Neither the name of The DragonFly Project nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
 */

#ifndef _ACXREG_H
#define _ACXREG_H

/*
 * IO register index
 */
#define ACXREG_SOFT_RESET		0
#define ACXREG_FWMEM_ADDR		1
#define ACXREG_FWMEM_DATA		2
#define ACXREG_FWMEM_CTRL		3
#define ACXREG_FWMEM_START		4
#define ACXREG_EVENT_MASK		5
#define ACXREG_INTR_TRIG		6
#define ACXREG_INTR_MASK		7
#define ACXREG_INTR_STATUS		8
#define ACXREG_INTR_STATUS_CLR		9	/* cleared after being read */
#define ACXREG_INTR_ACK			10
#define ACXREG_HINTR_TRIG		11	/* XXX what's this? */
#define ACXREG_RADIO_ENABLE		12
#define ACXREG_EEPROM_INIT		13
#define ACXREG_EEPROM_CTRL		14
#define ACXREG_EEPROM_ADDR		15
#define ACXREG_EEPROM_DATA		16
#define ACXREG_EEPROM_CONF		17
#define ACXREG_EEPROM_INFO		18
#define ACXREG_PHY_ADDR			19
#define ACXREG_PHY_DATA			20
#define ACXREG_PHY_CTRL			21
#define ACXREG_GPIO_OUT_ENABLE		22
#define ACXREG_GPIO_OUT			23
#define ACXREG_CMD_REG_OFFSET		24
#define ACXREG_INFO_REG_OFFSET		25
#define ACXREG_RESET_SENSE		26
#define ACXREG_ECPU_CTRL		27
#define ACXREG_MAX			28
#define ACXREG(reg, val)		[ACXREG_##reg] = val

/*
 * Value read from ACXREG_EEPROM_INFO
 * upper 8bits are radio type
 * lower 8bits are form factor
 */
#define ACX_EEINFO_RADIO_TYPE_SHIFT	8
#define ACX_EEINFO_RADIO_TYPE_MASK	(0xff << ACX_EEINFO_RADIO_TYPE_SHIFT)
#define ACX_EEINFO_FORM_FACTOR_MASK	0xff

#define ACX_EEINFO_HAS_RADIO_TYPE(info)	((info) & ACX_EEINFO_RADIO_TYPE_MASK)
#define ACX_EEINFO_RADIO_TYPE(info)	((info) >> ACX_EEINFO_RADIO_TYPE_SHIFT)
#define ACX_EEINFO_FORM_FACTOR(info)	((info) & ACX_EEINFO_FORM_FACTOR_MASK)

/*
 * Size of command register whose location is obtained
 * from ACXREG_CMD_REG_OFFSET IO register
 */
#define ACX_CMD_REG_SIZE		4	/* 4 bytes */

/*
 * Size of infomation register whose location is obtained
 * from ACXREG_INFO_REG_OFFSET IO register
 */
#define ACX_INFO_REG_SIZE		4	/* 4 bytes */

/*
 * Offset of EEPROM variables
 */
#define ACX_EE_VERSION_OFS		0x05

/*
 * Possible values for various IO registers
 */

/* ACXREG_SOFT_RESET */
#define ACXRV_SOFT_RESET		0x1

/* ACXREG_FWMEM_START */
#define ACXRV_FWMEM_START_OP		0x0

/* ACXREG_FWMEM_CTRL */
#define ACXRV_FWMEM_ADDR_AUTOINC	0x10000

/* ACXREG_EVENT_MASK */
#define ACXRV_EVENT_DISABLE		0x8000	/* XXX What's this?? */

/* ACXREG_INTR_TRIG */
#define ACXRV_TRIG_CMD_FINI		0x0001
#define ACXRV_TRIG_TX_FINI		0x0004

/* ACXREG_INTR_MASK */
#define ACXRV_INTR_RX_DATA		0x0001
#define ACXRV_INTR_TX_FINI		0x0002
#define ACXRV_INTR_TX_XFER		0x0004
#define ACXRV_INTR_RX_FINI		0x0008
#define ACXRV_INTR_DTIM			0x0010
#define ACXRV_INTR_BEACON		0x0020
#define ACXRV_INTR_TIMER		0x0040
#define ACXRV_INTR_KEY_MISS		0x0080
#define ACXRV_INTR_WEP_FAIL		0x0100
#define ACXRV_INTR_CMD_FINI		0x0200
#define ACXRV_INTR_INFO			0x0400
#define ACXRV_INTR_OVERFLOW		0x0800	/* XXX */
#define ACXRV_INTR_PROC_ERR		0x1000	/* XXX */
#define ACXRV_INTR_SCAN_FINI		0x2000
#define ACXRV_INTR_FCS_THRESH		0x4000	/* XXX */
#define ACXRV_INTR_UNKN			0x8000
#define ACXRV_INTR_ALL			0xffff

/* ACXREG_EEPROM_INIT */
#define ACXRV_EEPROM_INIT		0x1

/* ACXREG_EEPROM_CTRL */
#define ACXRV_EEPROM_READ		0x2

/* ACXREG_PHY_CTRL */
#define ACXRV_PHY_WRITE			0x1
#define ACXRV_PHY_READ			0x2

/* ACXREG_PHY_ADDR */
#define ACXRV_PHYREG_TXPOWER		0x11	/* axc100 */
#define ACXRV_PHYREG_SENSITIVITY	0x30

/* ACXREG_ECPU_CTRL */
#define ACXRV_ECPU_HALT			0x1
#define ACXRV_ECPU_START		0x0

/* Commands */
#define ACXCMD_GET_CONF		0x01
#define ACXCMD_SET_CONF		0x02
#define ACXCMD_ENABLE_RXCHAN	0x03
#define ACXCMD_ENABLE_TXCHAN	0x04
#define ACXCMD_TMPLT_TIM	0x0a
#define ACXCMD_JOIN_BSS		0x0b
#define ACXCMD_WEP_MGMT		0x0c	/* acx111 */
#define ACXCMD_SLEEP		0x0f
#define ACXCMD_WAKEUP		0x10
#define ACXCMD_INIT_MEM		0x12	/* acx100 */
#define ACXCMD_TMPLT_BEACON	0x13
#define ACXCMD_TMPLT_PROBE_RESP	0x14
#define ACXCMD_TMPLT_NULL_DATA	0x15
#define ACXCMD_TMPLT_PROBE_REQ	0x16
#define ACXCMD_INIT_RADIO	0x18

#if 0
/*
 * acx111 does not agree with acx100 about
 * the meaning of following values.  So they
 * are put into chip specific files.
 */
#define ACX_CONF_FW_RING	0x0003
#define ACX_CONF_MEMOPT		0x0005
#endif
#define ACX_CONF_MEMBLK_SIZE	0x0004	/* acx100 */
#define ACX_CONF_RATE_FALLBACK	0x0006
#define ACX_CONF_WEPOPT		0x0007	/* acx100 */
#define ACX_CONF_MMAP		0x0008
#define ACX_CONF_FWREV		0x000d
#define ACX_CONF_RXOPT		0x0010
#define ACX_CONF_OPTION		0x0015	/* acx111 */
#define ACX_CONF_EADDR		0x1001
#define ACX_CONF_NRETRY_SHORT	0x1005
#define ACX_CONF_NRETRY_LONG	0x1006
#define ACX_CONF_WEPKEY		0x1007	/* acx100 */
#define ACX_CONF_MSDU_LIFETIME	0x1008
#define ACX_CONF_REGDOM		0x100a
#define ACX_CONF_ANTENNA	0x100b
#define ACX_CONF_TXPOWER	0x100d	/* acx111 */
#define ACX_CONF_CCA_MODE	0x100e
#define ACX_CONF_ED_THRESH	0x100f
#define ACX_CONF_WEP_TXKEY	0x1010

/*
 * NOTE:
 * Following structs' fields are little endian
 */

struct acx_conf {
	uint16_t	conf_id;	/* see ACXCONF_ (_acxcmd.h) */
	uint16_t	conf_data_len;
} __packed;

struct acx_conf_mmap {
	struct acx_conf	confcom;
	uint32_t	code_start;
	uint32_t	code_end;
	uint32_t	wep_cache_start;
	uint32_t	wep_cache_end;
	uint32_t	pkt_tmplt_start;
	uint32_t	pkt_tmplt_end;
	uint32_t	fw_desc_start;
	uint32_t	fw_desc_end;
	uint32_t	memblk_start;
	uint32_t	memblk_end;
} __packed;

struct acx_conf_wepopt {
	struct acx_conf	confcom;
	uint16_t	nkey;
	uint8_t		opt;	/* see WEPOPT_ */
} __packed;

#define WEPOPT_HDWEP	0	/* hardware WEP */

struct acx_conf_eaddr {
	struct acx_conf	confcom;
	uint8_t		eaddr[IEEE80211_ADDR_LEN];
} __packed;

struct acx_conf_regdom {
	struct acx_conf	confcom;
	uint8_t		regdom;
	uint8_t		unknown;
} __packed;

struct acx_conf_antenna {
	struct acx_conf	confcom;
	uint8_t		antenna;
} __packed;

struct acx_conf_fwrev {
	struct acx_conf	confcom;
#define ACX_FWREV_LEN	20
	/*
	 * "Rev xx.xx.xx.xx"
	 * '\0' terminated
	 */
	char		fw_rev[ACX_FWREV_LEN];
	uint32_t	hw_id;
} __packed;

struct acx_conf_nretry_long {
	struct acx_conf	confcom;
	uint8_t		nretry;
} __packed;

struct acx_conf_nretry_short {
	struct acx_conf	confcom;
	uint8_t		nretry;
} __packed;

struct acx_conf_msdu_lifetime {
	struct acx_conf	confcom;
	uint32_t	lifetime;
} __packed;

struct acx_conf_rate_fallback {
	struct acx_conf	confcom;
	uint8_t		ratefb_enable;	/* 0/1 */
} __packed;

struct acx_conf_rxopt {
	struct acx_conf	confcom;
	uint16_t	opt1;	/* see RXOPT1_ */
	uint16_t	opt2;	/* see RXOPT2_ */
} __packed;

#define RXOPT1_INCL_RXBUF_HDR	0x2000	/* rxbuf with acx_rxbuf_hdr */
#define RXOPT1_RECV_SSID	0x0400	/* recv frame for joined SSID */
#define RXOPT1_FILT_BCAST	0x0200	/* filt broadcast pkt */
#define RXOPT1_RECV_MCAST1	0x0100	/* recv pkt for multicast addr1 */
#define RXOPT1_RECV_MCAST0	0x0080	/* recv pkt for multicast addr0 */
#define RXOPT1_FILT_ALLMULTI	0x0040	/* filt allmulti pkt */
#define RXOPT1_FILT_FSSID	0x0020	/* filt frame for foreign SSID */
#define RXOPT1_FILT_FDEST	0x0010	/* filt frame for foreign dest addr */
#define RXOPT1_PROMISC		0x0008	/* promisc mode */
#define RXOPT1_INCL_FCS		0x0004
#define RXOPT1_INCL_PHYHDR	0x0000	/* XXX 0x0002 */

#define RXOPT2_RECV_ASSOC_REQ	0x0800
#define RXOPT2_RECV_AUTH	0x0400
#define RXOPT2_RECV_BEACON	0x0200
#define RXOPT2_RECV_CF		0x0100
#define RXOPT2_RECV_CTRL	0x0080
#define RXOPT2_RECV_DATA	0x0040
#define RXOPT2_RECV_BROKEN	0x0020	/* broken frame */
#define RXOPT2_RECV_MGMT	0x0010
#define RXOPT2_RECV_PROBE_REQ	0x0008
#define RXOPT2_RECV_PROBE_RESP	0x0004
#define RXOPT2_RECV_ACK		0x0002	/* RTS/CTS/ACK */
#define RXOPT2_RECV_OTHER	0x0001

struct acx_conf_wep_txkey {
	struct acx_conf	confcom;
	uint8_t		wep_txkey;
} __packed;

struct acx_tmplt_null_data {
	uint16_t	size;
	struct ieee80211_frame data;
} __packed;

struct acx_tmplt_probe_req {
	uint16_t	size;
	union {
		struct {
			struct ieee80211_frame f;
			uint8_t		var[1];
		} __packed	u_data;
		uint8_t		u_mem[0x44];
	}		data;
} __packed;

#define ACX_TMPLT_PROBE_REQ_SIZ(var_len)	\
	(sizeof(uint16_t) + sizeof(struct ieee80211_frame) + (var_len))

struct acx_tmplt_probe_resp {
	uint16_t	size;
	union {
		struct {
			struct ieee80211_frame f;
			uint8_t		time_stamp[8];
			uint16_t	beacon_intvl;
			uint16_t	cap;
			uint8_t		var[1];
		} __packed	u_data;
		uint8_t		u_mem[0x54];
	}		data;
} __packed;

#define ACX_TMPLT_PROBE_RESP_SIZ(var_len)				\
	(sizeof(uint16_t) + sizeof(struct ieee80211_frame) +		\
	 8 * sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t) + (var_len))

/* XXX same as acx_tmplt_probe_resp */
struct acx_tmplt_beacon {
	uint16_t	size;
	union {
		struct {
			struct ieee80211_frame f;
			uint8_t		time_stamp[8];
			uint16_t	beacon_intvl;
			uint16_t	cap;
			uint8_t		var[1];
		} __packed	u_data;
		uint8_t		u_mem[0x54];
	}		data;
} __packed;

/* XXX C&P of ACX_TMPLT_PROVE_RESP_SIZ() */
#define ACX_TMPLT_BEACON_SIZ(var_len)					\
	(sizeof(uint16_t) + sizeof(struct ieee80211_frame) +		\
	 8 * sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t) + (var_len))

/* XXX do NOT belong here */
struct tim_head {
	uint8_t	eid;
	uint8_t	len;
	uint8_t	dtim_count;
	uint8_t	dtim_period;
	uint8_t	bitmap_ctrl;
} __packed;

/* For tim_head.len (tim_head - eid - len + bitmap) */
#define ACX_TIM_LEN(bitmap_len)	\
	(sizeof(struct tim_head) - (2 * sizeof(uint8_t)) + (bitmap_len))
#define ACX_TIM_BITMAP_LEN	1

struct acx_tmplt_tim {
	uint16_t	size;
	union {
		struct {
			struct tim_head	th;
			uint8_t		bitmap[1];
		} __packed	u_data;
		uint8_t		u_mem[0x100];
	}		data;
#define tim_eid		data.u_data.th.eid
#define tim_len		data.u_data.th.len
#define tim_dtim_count	data.u_data.th.dtim_count
#define tim_dtim_period	data.u_data.th.dtim_period
#define tim_bitmap_ctrl	data.u_data.th.bitmap_ctrl
#define tim_bitmap	data.u_data.bitmap
} __packed;

#define ACX_TMPLT_TIM_SIZ(bitmap_len)	\
	(sizeof(uint16_t) + sizeof(struct tim_head) + (bitmap_len))

#define CMDPRM_WRITE_REGION_1(sc, r, rlen)		\
	bus_space_write_region_1((sc)->sc_mem2_bt,	\
				 (sc)->sc_mem2_bh,	\
				 (sc)->sc_cmd_param,	\
				 (const uint8_t *)(r), (rlen))

#define CMDPRM_READ_REGION_1(sc, r, rlen)				\
	bus_space_read_region_1((sc)->sc_mem2_bt, (sc)->sc_mem2_bh,	\
				(sc)->sc_cmd_param, (uint8_t *)(r), (rlen))

/*
 * This will clear previous command's
 * execution status too
 */
#define CMD_WRITE_4(sc, val)					\
	bus_space_write_4((sc)->sc_mem2_bt, (sc)->sc_mem2_bh,	\
			  (sc)->sc_cmd, (val))
#define CMD_READ_4(sc)		\
	bus_space_read_4((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, (sc)->sc_cmd)

/*
 * acx command register layerout:
 * upper 16bits are command execution status
 * lower 16bits are command to be executed
 */
#define ACX_CMD_STATUS_SHIFT	16
#define ACX_CMD_STATUS_OK	1

struct radio_init {
	uint32_t	radio_ofs;	/* radio firmware offset */
	uint32_t	radio_len;	/* radio firmware length */
} __packed;

struct bss_join_hdr {
	uint8_t		bssid[IEEE80211_ADDR_LEN];
	uint16_t	beacon_intvl;
	uint8_t		chip_spec[3];
	uint8_t		ndata_txrate;	/* see ACX_NDATA_TXRATE_ */
	uint8_t		ndata_txopt;	/* see ACX_NDATA_TXOPT_ */
	uint8_t		mode;		/* see ACX_MODE_ */
	uint8_t		channel;
	uint8_t		esslen;
	char		essid[1];
} __packed;

/*
 * non-data frame tx rate
 */
#define ACX_NDATA_TXRATE_1		10	/* 1Mbits/s */
#define ACX_NDATA_TXRATE_2		20	/* 2Mbits/s */

/*
 * non-data frame tx options
 */
#define ACX_NDATA_TXOPT_PBCC		0x40
#define ACX_NDATA_TXOPT_OFDM		0x20
#define ACX_NDATA_TXOPT_SHORT_PREAMBLE	0x10

#define BSS_JOIN_BUFLEN		\
	(sizeof(struct bss_join_hdr) + IEEE80211_NWID_LEN - 1)
#define BSS_JOIN_PARAM_SIZE(bj)	\
	(sizeof(struct bss_join_hdr) + (bj)->esslen - 1)

#define PCIR_BAR(x)     (PCI_MAPS + (x) * 4)

#endif	/* !_ACXREG_H */

license: unknown


code:
/*	$OpenBSD: acxvar.h,v 1.19 2008/07/21 04:12:21 kevlo Exp $ */

/*
 * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
 *
 * 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 The DragonFly Project.  All rights reserved.
 *
 * This code is derived from software contributed to The DragonFly Project
 * by Sepherosa Ziehau <sepherosa@gmail.com>
 *
 * 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. Neither the name of The DragonFly Project nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
 */

#ifndef _IF_ACXVAR_H
#define _IF_ACXVAR_H

#ifdef ACX_DEBUG
extern int acxdebug;
#define DPRINTF(x)      do { if (acxdebug) printf x; } while (0)
#define DPRINTFN(n,x)   do { if (acxdebug >= (n)) printf x; } while (0)
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif

#define ACX_FRAME_HDRLEN	sizeof(struct ieee80211_frame)
#define ACX_MEMBLOCK_SIZE	256

#define ACX_TX_DESC_CNT		16
#define ACX_RX_DESC_CNT		16

#define ACX_TX_RING_SIZE	\
	(2 * ACX_TX_DESC_CNT * sizeof(struct acx_host_desc))
#define ACX_RX_RING_SIZE	\
	(ACX_RX_DESC_CNT * sizeof(struct acx_host_desc))

#define CSR_READ_1(sc, reg)					\
	bus_space_read_1((sc)->sc_mem1_bt, (sc)->sc_mem1_bh,	\
			 (sc)->chip_ioreg[(reg)])
#define CSR_READ_2(sc, reg)					\
	bus_space_read_2((sc)->sc_mem1_bt, (sc)->sc_mem1_bh,	\
			 (sc)->chip_ioreg[(reg)])
#define CSR_READ_4(sc, reg)					\
	bus_space_read_4((sc)->sc_mem1_bt, (sc)->sc_mem1_bh,	\
			 (sc)->chip_ioreg[(reg)])

#define CSR_WRITE_2(sc, reg, val)				\
	bus_space_write_2((sc)->sc_mem1_bt, (sc)->sc_mem1_bh,	\
			  (sc)->chip_ioreg[(reg)], val)
#define CSR_WRITE_4(sc, reg, val)				\
	bus_space_write_4((sc)->sc_mem1_bt, (sc)->sc_mem1_bh,	\
			  (sc)->chip_ioreg[(reg)], val)

#define CSR_SETB_2(sc, reg, b)		\
	CSR_WRITE_2((sc), (reg), CSR_READ_2((sc), (reg)) | (b))
#define CSR_CLRB_2(sc, reg, b)		\
	CSR_WRITE_2((sc), (reg), CSR_READ_2((sc), (reg)) & (~(b)))

#define DESC_WRITE_REGION_1(sc, off, d, dlen)				\
	bus_space_write_region_1((sc)->sc_mem2_bt, (sc)->sc_mem2_bh,	\
				 (off),	(const uint8_t *)(d), (dlen))

#define FW_TXDESC_SETFIELD_1(sc, mb, field, val)		\
	bus_space_write_1((sc)->sc_mem2_bt, (sc)->sc_mem2_bh,	\
	    (mb)->tb_fwdesc_ofs + offsetof(struct acx_fw_txdesc, field), (val))
#define FW_TXDESC_SETFIELD_2(sc, mb, field, val)		\
	bus_space_write_2((sc)->sc_mem2_bt, (sc)->sc_mem2_bh,	\
	    (mb)->tb_fwdesc_ofs + offsetof(struct acx_fw_txdesc, field), (val))
#define FW_TXDESC_SETFIELD_4(sc, mb, field, val)	\
	bus_space_write_4((sc)->sc_mem2_bt, (sc)->sc_mem2_bh,	\
	    (mb)->tb_fwdesc_ofs + offsetof(struct acx_fw_txdesc, field), (val))

#define FW_TXDESC_GETFIELD_1(sc, mb, field)			\
	bus_space_read_1((sc)->sc_mem2_bt, (sc)->sc_mem2_bh,	\
	    (mb)->tb_fwdesc_ofs + offsetof(struct acx_fw_txdesc, field))

/*
 * Firmware TX descriptor
 * Fields are little endian
 */
struct acx_fw_txdesc {
	uint32_t	f_tx_next_desc;	/* next acx_fw_txdesc phyaddr */
	uint32_t	f_tx_host_desc;	/* acx_host_desc phyaddr */
	uint32_t	f_tx_acx_ptr;
	uint32_t	f_tx_time;
	uint16_t	f_tx_len;
	uint16_t	f_tx_reserved;

	uint32_t	f_tx_dev_spec[4];

	uint8_t		f_tx_ctrl;	/* see DESC_CTRL_ */
	uint8_t		f_tx_ctrl2;
	uint8_t		f_tx_error;	/* see DESC_ERR_ */
	uint8_t		f_tx_ack_fail;
	uint8_t		f_tx_rts_fail;
	uint8_t		f_tx_rts_ok;

	/* XXX should be moved to chip specific file */
	union {
		struct {
			uint8_t		rate100;	/* acx100 tx rate */
			uint8_t		queue_ctrl;
		} __packed r1;
		struct {
			uint16_t	rate111;	/* acx111 tx rate */
		} __packed r2;
	} u;
#define f_tx_rate100	u.r1.rate100
#define f_tx_queue_ctrl	u.r1.queue_ctrl
#define f_tx_rate111	u.r2.rate111
	uint32_t	f_tx_queue_info;
} __packed;

/*
 * Firmware RX descriptor
 * Fields are little endian
 */
struct acx_fw_rxdesc {
	uint32_t	f_rx_next_desc;	/* next acx_fw_rxdesc phyaddr */
	uint32_t	f_rx_host_desc;	/* acx_host_desc phyaddr */
	uint32_t	f_rx_acx_ptr;
	uint32_t	f_rx_time;
	uint16_t	f_rx_len;
	uint16_t	f_rx_wep_len;
	uint32_t	f_rx_wep_ofs;

	uint8_t		f_rx_dev_spec[16];

	uint8_t		f_rx_ctrl;	/* see DESC_CTRL_ */
	uint8_t		f_rx_rate;
	uint8_t		f_rx_error;
	uint8_t		f_rx_snr;	/* signal noise ratio */
	uint8_t		f_rx_level;
	uint8_t		f_rx_queue_ctrl;
	uint16_t	f_rx_unknown0;
	uint32_t	f_rx_unknown1;
} __packed;

/*
 * Host TX/RX descriptor
 * Fields are little endian
 */
struct acx_host_desc {
	uint32_t	h_data_paddr;	/* data phyaddr */
	uint16_t	h_data_ofs;
	uint16_t	h_reserved;
	uint16_t	h_ctrl;		/* see DESC_CTRL_ */
	uint16_t	h_data_len;	/* data length */
	uint32_t	h_next_desc;	/* next acx_host_desc phyaddr */
	uint32_t	h_pnext;
	uint32_t	h_status;	/* see DESC_STATUS_ */
} __packed;

#define DESC_STATUS_FULL		0x80000000

#define DESC_CTRL_SHORT_PREAMBLE	0x01
#define DESC_CTRL_FIRST_FRAG		0x02
#define DESC_CTRL_AUTODMA		0x04
#define DESC_CTRL_RECLAIM		0x08
#define DESC_CTRL_HOSTDONE		0x20	/* host finished buf proc */
#define DESC_CTRL_ACXDONE		0x40	/* chip finished buf proc */
#define DESC_CTRL_HOSTOWN		0x80	/* host controls desc */

#define DESC_ERR_OTHER_FRAG		0x01
#define DESC_ERR_ABORT			0x02
#define DESC_ERR_PARAM			0x04
#define DESC_ERR_NO_WEPKEY		0x08
#define DESC_ERR_MSDU_TIMEOUT		0x10
#define DESC_ERR_EXCESSIVE_RETRY	0x20
#define DESC_ERR_BUF_OVERFLOW		0x40
#define DESC_ERR_DMA			0x80

/*
 * Extra header in receiving buffer
 * Fields are little endian
 */
struct acx_rxbuf_hdr {
	uint16_t	rbh_len;	/* ACX_RXBUG_LEN_MASK part is len */
	uint8_t		rbh_memblk_cnt;
	uint8_t		rbh_status;
	uint8_t		rbh_stat_baseband; /* see ACX_RXBUF_STAT_ */
	uint8_t		rbh_plcp;
	uint8_t		rbh_level;	/* signal level */
	uint8_t		rbh_snr;	/* signal noise ratio */
	uint32_t	rbh_time;	/* recv timestamp */

	/*
	 * XXX may have 4~8 byte here which
	 * depends on firmware version
	 */
} __packed;

#define ACX_RXBUF_LEN_MASK	0xfff
#define ACX_RXBUF_STAT_LNA	0x80	/* low noise amplifier */

struct acx_ring_data {
	struct acx_host_desc	*rx_ring;
	bus_dma_segment_t	rx_ring_seg;
	bus_dmamap_t		rx_ring_dmamap;
	uint32_t		rx_ring_paddr;

	struct acx_host_desc	*tx_ring;
	bus_dma_segment_t	tx_ring_seg;
	bus_dmamap_t		tx_ring_dmamap;
	uint32_t		tx_ring_paddr;
};

struct acx_txbuf {
	struct mbuf		*tb_mbuf;
	bus_dmamap_t		tb_mbuf_dmamap;

	struct acx_host_desc	*tb_desc1;
	struct acx_host_desc	*tb_desc2;

	uint32_t		tb_fwdesc_ofs;

	/*
	 * Used by tx rate updating
	 */
	struct acx_node		*tb_node;	/* remote node */
	int			tb_rate;	/* current tx rate */
};

struct acx_rxbuf {
	struct mbuf		*rb_mbuf;
	bus_dmamap_t		rb_mbuf_dmamap;

	struct acx_host_desc	*rb_desc;
};

struct acx_buf_data {
	struct acx_rxbuf	rx_buf[ACX_RX_DESC_CNT];
	struct acx_txbuf	tx_buf[ACX_TX_DESC_CNT];
	bus_dmamap_t		mbuf_tmp_dmamap;

	int			rx_scan_start;

	int			tx_free_start;
	int			tx_used_start;
	int			tx_used_count;
};

struct acx_node {
	struct ieee80211_node		ni;	/* must be first */
	struct ieee80211_amrr_node	amn;
};

struct acx_config {
	uint8_t	antenna;
	uint8_t	regdom;
	uint8_t	cca_mode;	/* acx100 */
	uint8_t	ed_thresh;	/* acx100 */
};

struct acx_stats {
	uint64_t	err_oth_frag;	/* XXX error in other frag?? */
	uint64_t	err_abort;	/* tx abortion */
	uint64_t	err_param;	/* tx desc contains invalid param */
	uint64_t	err_no_wepkey;	/* no WEP key exists */
	uint64_t	err_msdu_timeout; /* MSDU timed out */
	uint64_t	err_ex_retry;	/* excessive tx retry */
	uint64_t	err_buf_oflow;	/* buffer overflow */
	uint64_t	err_dma;	/* DMA error */
	uint64_t	err_unkn;	/* XXX unknown error */
};

#define ACX_RX_RADIOTAP_PRESENT						\
	((1 << IEEE80211_RADIOTAP_FLAGS) |				\
	 (1 << IEEE80211_RADIOTAP_CHANNEL) |				\
	 (1 << IEEE80211_RADIOTAP_RSSI))

struct acx_rx_radiotap_hdr {
	struct ieee80211_radiotap_header	wr_ihdr;
	uint8_t					wr_flags;
	uint16_t				wr_chan_freq;
	uint16_t				wr_chan_flags;
	uint8_t					wr_rssi;
	uint8_t					wr_max_rssi;
} __packed;

#define ACX_TX_RADIOTAP_PRESENT						\
	((1 << IEEE80211_RADIOTAP_FLAGS) |				\
	 (1 << IEEE80211_RADIOTAP_RATE) |				\
	 (1 << IEEE80211_RADIOTAP_CHANNEL))				\

struct acx_tx_radiotap_hdr {
	struct ieee80211_radiotap_header	wt_ihdr;
	uint8_t					wt_flags;
	uint8_t					wt_rate;
	uint16_t				wt_chan_freq;
	uint16_t				wt_chan_flags;
} __packed;

struct acx_softc {
	/*
	 * sc_xxx are filled in by common code
	 * chip_xxx are filled in by chip specific code
	 */
	struct device		sc_dev;
	struct ieee80211com	sc_ic;

	struct timeout		sc_chanscan_timer;
	uint32_t		sc_flags;	/* see ACX_FLAG_ */

	uint32_t		sc_firmware_ver;
	uint32_t		sc_hardware_id;

	bus_dma_tag_t		sc_dmat;

	struct ieee80211_amrr	amrr;
	struct timeout		amrr_ch;

	/*
	 * MMIO 1
	 */
	bus_space_tag_t		sc_mem1_bt;
	bus_space_handle_t	sc_mem1_bh;
	int			chip_mem1_rid;

	/*
	 * MMIO 2
	 */
	bus_space_tag_t		sc_mem2_bt;
	bus_space_handle_t	sc_mem2_bh;
	int			chip_mem2_rid;

	int			(*sc_enable)(struct acx_softc *);
	void			(*sc_disable)(struct acx_softc *);
	void			(*sc_power)(struct acx_softc *, int);

	uint32_t		sc_cmd;		/* cmd reg (MMIO 2) */
	uint32_t		sc_cmd_param;	/* cmd param reg (MMIO 2) */
	uint32_t		sc_info;	/* unused */
	uint32_t		sc_info_param;	/* unused */

	const uint16_t		*chip_ioreg;	/* reg map (MMIO 1) */

	/*
	 * NOTE:
	 * chip_intr_enable is not necessarily same as
	 * ~chip_intr_disable
	 */
	uint16_t		chip_intr_enable;
	uint16_t		chip_intr_disable;

	int			chip_hw_crypt;
	uint16_t		chip_gpio_pled;	/* power led */
	uint16_t		chip_chan_flags; /* see IEEE80211_CHAN_ */
	uint16_t		chip_txdesc1_len;
	int			chip_rxbuf_exhdr; /* based on fw ver */
	uint32_t		chip_ee_eaddr_ofs;
	enum ieee80211_phymode	chip_phymode;	/* see IEEE80211_MODE_ */
	uint8_t			chip_fw_txdesc_ctrl;

	uint8_t			sc_eeprom_ver;	/* unused */
	uint8_t			sc_form_factor;	/* unused */
	uint8_t			sc_radio_type;	/* see ACX_RADIO_TYPE_ */

	struct acx_ring_data	sc_ring_data;
	struct acx_buf_data	sc_buf_data;

	struct acx_stats	sc_stats;	/* statistics */

	/*
	 * Per interface sysctl variables
	 */
	int			sc_txtimer;
	int			sc_long_retry_limit;
	int			sc_short_retry_limit;
	int			sc_msdu_lifetime;

	int			(*sc_newstate)
				(struct ieee80211com *,
				 enum ieee80211_state, int);

	int			(*chip_init)		/* non-NULL */
				(struct acx_softc *);

	int			(*chip_set_wepkey)
				(struct acx_softc *,
				 struct ieee80211_key *, int);

	int			(*chip_read_config)
				(struct acx_softc *, struct acx_config *);

	int			(*chip_write_config)
				(struct acx_softc *, struct acx_config *);

	void			(*chip_set_fw_txdesc_rate) /* non-NULL */
				(struct acx_softc *, struct acx_txbuf *, int);

	void			(*chip_set_bss_join_param) /* non-NULL */
				(struct acx_softc *, void *, int);

	void			(*chip_proc_wep_rxbuf)
				(struct acx_softc *, struct mbuf *, int *);

#if NBPFILTER > 0
	caddr_t			sc_drvbpf;

	union {
		struct acx_rx_radiotap_hdr th;
		uint8_t pad[64];
	}			sc_rxtapu;
#define sc_rxtap		sc_rxtapu.th
	int			sc_rxtap_len;

	union {
		struct acx_tx_radiotap_hdr th;
		uint8_t pad[64];
	}			sc_txtapu;
#define sc_txtap		sc_txtapu.th
	int			sc_txtap_len;
#endif
};

#define ACX_FLAG_FW_LOADED	0x01
#define ACX_FLAG_ACX111		0x02

#define ACX_RADIO_TYPE_MAXIM	0x0d
#define ACX_RADIO_TYPE_RFMD	0x11
#define ACX_RADIO_TYPE_RALINK	0x15
#define ACX_RADIO_TYPE_RADIA	0x16
#define ACX_RADIO_TYPE_UNKN17	0x17
#define ACX_RADIO_TYPE_UNKN19	0x19

#define ACX_RADIO_RSSI_MAXIM	120	/* 100dB */
#define ACX_RADIO_RSSI_RFMD	215	/* 215dB */
#define ACX_RADIO_RSSI_RALINK	0	/* XXX unknown yet */
#define ACX_RADIO_RSSI_RADIA	78	/* 78db */
#define ACX_RADIO_RSSI_UNKN	0	/* unknown radio */

extern const struct ieee80211_rateset	acx_rates_11b;
extern const struct ieee80211_rateset	acx_rates_11g;
extern int				acx_beacon_intvl;

void	acx100_set_param(struct acx_softc *);
void	acx111_set_param(struct acx_softc *);

int	acx_init_tmplt_ordered(struct acx_softc *);
void	acx_write_phyreg(struct acx_softc *, uint32_t, uint8_t);

int	acx_set_tmplt(struct acx_softc *, uint16_t, void *, uint16_t);
int	acx_get_conf(struct acx_softc *, uint16_t, void *, uint16_t);
int	acx_set_conf(struct acx_softc *, uint16_t, void *, uint16_t);
int	acx_exec_command(struct acx_softc *, uint16_t, void *, uint16_t,
	    void *, uint16_t);
int	acx_attach(struct acx_softc *);
int	acx_detach(void *);
int	acx_intr(void *);

#endif	/* !_IF_ACXVAR_H */


code:
/*	$OpenBSD: ad1843reg.h,v 1.2 2009/11/18 21:13:17 jakemsr Exp $	*/

/*
 * Copyright (c) 2005 Mark Kettenis
 *
 * 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.
 */

/*
 * AD1843 Codec register defenitions.
 */

#define AD1843_CODEC_STATUS		0
#define  AD1843_INIT			0x8000
#define  AD1843_PDNO			0x4000
#define  AD1843_REVISION_MASK		0x000f

#define AD1843_ADC_SOURCE_GAIN		2
#define  AD1843_LSS_MASK		0xe000
#define  AD1843_LSS_SHIFT		13
#define  AD1843_RSS_MASK		0x00e0
#define  AD1843_RSS_SHIFT		5
#define  AD1843_LMGE			0x1000
#define  AD1843_RMGE			0x0010
#define  AD1843_LIG_MASK		0x0f00
#define  AD1843_LIG_SHIFT		8
#define  AD1843_RIG_MASK		0x000f
#define  AD1843_RIG_SHIFT		0

#define AD1843_DAC2_TO_MIXER		3
#define  AD1843_LD2MM			0x8000
#define  AD1843_RD2MM			0x0080
#define  AD1843_LD2M_MASK		0x1f00
#define  AD1843_LD2M_SHIFT		8
#define  AD1843_RD2M_MASK		0x001f
#define  AD1843_RD2M_SHIFT		0

#define AD1843_MISC_SETTINGS		8
#define  AD1843_MNMM			0x8000
#define  AD1843_MNM_MASK		0x1f00
#define  AD1843_MNM_SHIFT		8
#define  AD1843_ALLMM			0x0080
#define  AD1843_MNOM			0x0040
#define  AD1843_HPOM			0x0020
#define  AD1843_HPOS			0x0010
#define  AD1843_SUMM			0x0008
#define  AD1843_DAC2T			0x0002
#define  AD1843_DAC1T			0x0001

#define AD1843_DAC1_ANALOG_GAIN		9
#define  AD1843_LDA1GM			0x8000
#define  AD1843_RDA1GM			0x0080
#define  AD1843_LDA1G_MASK		0x3f00
#define  AD1843_LDA1G_SHIFT		8
#define  AD1843_RDA1G_MASK		0x003f
#define  AD1843_RDA1G_SHIFT		0

#define AD1843_DAC1_DIGITAL_GAIN	11
#define  AD1843_LDA1AM			0x8000
#define  AD1843_RDA1AM			0x0080

#define AD1843_CHANNEL_SAMPLE_RATE	15
#define  AD1843_DA1C_SHIFT		8
#define  AD1843_ADRC_SHIFT		2
#define  AD1843_ADLC_SHIFT		0

#define AD1843_CLOCK1_SAMPLE_RATE	17
#define AD1843_CLOCK2_SAMPLE_RATE	20
#define AD1843_CLOCK3_SAMPLE_RATE	13

#define AD1843_SERIAL_INTERFACE		26
#define  AD1843_DA2F_MASK		0x0c00
#define  AD1843_DA2F_SHIFT		10
#define  AD1843_DA1F_MASK		0x0300
#define  AD1843_DA1F_SHIFT		8
#define  AD1843_ADTLK			0x0010
#define  AD1843_ADRF_MASK		0x000c
#define  AD1843_ADRF_SHIFT		2
#define  AD1843_ADLF_MASK		0x0003
#define  AD1843_ADLF_SHIFT		0
#define  AD1843_PCM8			0
#define  AD1843_PCM16			1
#define  AD1843_ULAW			2
#define  AD1843_ALAW			3
#define  AD1843_SCF			0x0080

#define AD1843_CHANNEL_POWER_DOWN	27
#define  AD1843_DFREE			0x8000
#define  AD1843_DDMEN			0x1000
#define  AD1843_DA2EN			0x0200
#define  AD1843_DA1EN			0x0100
#define  AD1843_ANAEN			0x0080
#define  AD1843_HPEN			0x0040
#define  AD1843_AAMEN			0x0010
#define  AD1843_ADREN			0x0002
#define  AD1843_ADLEN			0x0001

#define AD1843_FUNDAMENTAL_SETTINGS	28
#define  AD1843_PDNI			0x8000
#define  AD1843_ACEN			0x4000
#define  AD1843_C3EN			0x2000
#define  AD1843_C2EN			0x1000
#define  AD1843_C1EN			0x0800

#define AD1843_NREGS			32


code:
/*	$OpenBSD: ad1848reg.h,v 1.6 1999/03/08 22:54:33 jason Exp $	*/
/*	$NetBSD: ad1848reg.h,v 1.4 1997/05/07 20:23:53 augustss Exp $	*/

/*
 * Copyright (c) 1994 John Brezak
 * Copyright (c) 1991-1993 Regents of the University of California.
 * 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 the Computer Systems
 *	Engineering Group at Lawrence Berkeley Laboratory.
 * 4. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
 *
 */
/*
 * Copyright (c) 1993 Analog Devices Inc. All rights reserved
 */

/*
 * Further documentation can be found at:
 *	http://www.cirrus.com/products/overviews/cs4231.html
 *	(the CS4231A is an augmented ad1848, additional registers, etc).
 */

/* parent driver is primarily responsible for checking this */
#define AD1848_BASE_VALID(base)	(((base) & 0x003) == 0)

/* AD1848 direct registers */
#define AD1848_IADDR		0x00
#define AD1848_IDATA		0x01
#define AD1848_STATUS		0x02
#define AD1848_PIO		0x03

/* Gain constants  */
#define GAIN_0			0x00
#define GAIN_1_5		0x01
#define GAIN_3			0x02
#define GAIN_4_5		0x03
#define GAIN_6			0x04
#define GAIN_7_5		0x05
#define GAIN_9			0x06
#define GAIN_10_5		0x07
#define GAIN_12			0x08
#define GAIN_13_5		0x09
#define GAIN_15			0x0a
#define GAIN_16_5		0x0b
#define GAIN_18			0x0c
#define GAIN_19_5		0x0d
#define GAIN_21			0x0e
#define GAIN_22_5		0x0f

/* Attenuation constants  */

#define ATTEN_0			0x00
#define ATTEN_1_5		0x01
#define ATTEN_3			0x02
#define ATTEN_4_5		0x03
#define ATTEN_6			0x04
#define ATTEN_7_5		0x05
#define ATTEN_9			0x06
#define ATTEN_10_5		0x07
#define ATTEN_12		0x08
#define ATTEN_13_5		0x09
#define ATTEN_15		0x0a
#define ATTEN_16_5		0x0b
#define ATTEN_18		0x0c
#define ATTEN_19_5		0x0d
#define ATTEN_21		0x0e
#define ATTEN_22_5		0x0f

/* AD1848 Sound Port bit defines */
#define SP_IN_INIT		0x80
#define MODE_CHANGE_ENABLE	0x40
#define TRANSFER_DISABLE	0x20
#define ADDRESS_MASK		0xe0

/* Status bits */
#define INTERRUPT_STATUS	0x01
#define PLAYBACK_READY		0x02
#define PLAYBACK_LEFT		0x04
/* pbright is not left */
#define PLAYBACK_UPPER		0x08
/* bplower is not upper */
#define SAMPLE_ERROR		0x10
#define CAPTURE_READY		0x20
#define CAPTURE_LEFT		0x40
/* cpright is not left */
#define CAPTURE_UPPER		0x80
/* cplower is not upper */

/* Input & Output regs bits */
#define LINE_INPUT		0x00
#define AUX_INPUT		0x40
#define MIC_INPUT		0x80
#define MIXED_DAC_INPUT		0xc0
#define INPUT_GAIN_MASK		0xf0
#define INPUT_MIC_GAIN_ENABLE	0x20
#define INPUT_SOURCE_MASK	0x3f
#define AUX_INPUT_ATTEN_BITS	0x1f
#define AUX_INPUT_ATTEN_MASK	0xe0
#define AUX_INPUT_MUTE		0x80
#define OUTPUT_MUTE		0x80
#define OUTPUT_ATTEN_BITS	0x3f
#define OUTPUT_ATTEN_MASK	0xc0

/* Clock and Data format reg bits (some also Capture Data format) */
#define CLOCK_XTAL2		0x01
#define CLOCK_XTAL1		0x00
#define CLOCK_FREQ_MASK		0xf1
#define FMT_MONO		0x00
#define FMT_STEREO		0x10
#define FORMAT_MASK		0x1f
#define FMT_PCM8		0x00	/* 8-bit unsigned */
#define FMT_ULAW		0x20	/* 8-bit mu-law */
#define FMT_TWOS_COMP		0x40	/* 16-bit signed */
#define FMT_ALAW		0x60	/* 8-bit alaw */
#define FMT_ADPCM		0xa0	/* IMA ADPCM */
#define FMT_TWOS_COMP_BE	0xc0	/* 16-bit signed, big endian */

/* Interface Configuration reg bits */
#define PLAYBACK_ENABLE		0x01
#define CAPTURE_ENABLE		0x02
#define DUAL_DMA		0x00
#define SINGLE_DMA		0x04
#define AUTO_CAL_ENABLE		0x08
#define PLAYBACK_PIO_ENABLE	0x40
#define CAPTURE_PIO_ENABLE	0x80

/* Pin control bits */
#define INTERRUPT_ENABLE	0x02
#define XCTL0_ENABLE		0x40
#define XCTL1_ENABLE		0x80

/* Test and init reg bits */
#define OVERRANGE_LEFT_MASK	0xfc
#define OVERRANGE_RIGHT_MASK	0xf3
#define DATA_REQUEST_STATUS	0x10
#define AUTO_CAL_IN_PROG	0x20
#define PLAYBACK_UNDERRUN	0x40
#define CAPTURE_OVERRUN		0x80

/* Miscellaneous Control reg bits */
#define ID_MASK			0x70
#define MODE2			0x40

/* Digital Mix Control reg bits */
#define DIGITAL_MIX1_ENABLE	0x01
#define MIX_ATTEN_MASK		0xfc

/* AD1848 Sound Port reg defines */
#define SP_LEFT_INPUT_CONTROL	0x00
#define SP_RIGHT_INPUT_CONTROL	0x01
#define SP_LEFT_AUX1_CONTROL	0x02
#define SP_RIGHT_AUX1_CONTROL	0x03
#define SP_LEFT_AUX2_CONTROL	0x04
#define SP_RIGHT_AUX2_CONTROL	0x05
#define SP_LEFT_OUTPUT_CONTROL	0x06
#define SP_RIGHT_OUTPUT_CONTROL 0x07
#define SP_CLOCK_DATA_FORMAT	0x08
#define SP_INTERFACE_CONFIG	0x09
#define SP_PIN_CONTROL		0x0A
#define SP_TEST_AND_INIT	0x0B
#define SP_MISC_INFO		0x0C
#define SP_DIGITAL_MIX		0x0D
#define SP_UPPER_BASE_COUNT	0x0E
#define SP_LOWER_BASE_COUNT	0x0F


code:
/*	$OpenBSD: adv.c,v 1.36 2014/09/14 14:17:24 jsg Exp $	*/
/*	$NetBSD: adv.c,v 1.6 1998/10/28 20:39:45 dante Exp $	*/

/*
 * Generic driver for the Advanced Systems Inc. Narrow SCSI controllers
 *
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * Author: Baldassare Dante Profeta <dante@mclink.it>
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS
 * 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.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/buf.h>

#include <machine/bus.h>
#include <machine/intr.h>

#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>

#include <dev/ic/adv.h>
#include <dev/ic/advlib.h>

/* #define ASC_DEBUG */

/******************************************************************************/

static int adv_alloc_ccbs(ASC_SOFTC *);
static int adv_create_ccbs(ASC_SOFTC *, ADV_CCB *, int);
void adv_ccb_free(void *, void *);
static void adv_reset_ccb(ADV_CCB *);
static int adv_init_ccb(ASC_SOFTC *, ADV_CCB *);
void *adv_ccb_alloc(void *);
static void adv_queue_ccb(ASC_SOFTC *, ADV_CCB *);
static void adv_start_ccbs(ASC_SOFTC *);

static u_int8_t *adv_alloc_overrunbuf(char *dvname, bus_dma_tag_t);

static void adv_scsi_cmd(struct scsi_xfer *);
static void advminphys(struct buf *, struct scsi_link *);
static void adv_narrow_isr_callback(ASC_SOFTC *, ASC_QDONE_INFO *);

static int adv_poll(ASC_SOFTC *, struct scsi_xfer *, int);
static void adv_timeout(void *);
static void adv_watchdog(void *);

/******************************************************************************/

struct cfdriver adv_cd = {
	NULL, "adv", DV_DULL
};

struct scsi_adapter adv_switch =
{
	adv_scsi_cmd,		/* called to start/enqueue a SCSI command */
	advminphys,		/* to limit the transfer to max device can do */
	0,			/* IT SEEMS IT IS NOT USED YET */
	0,			/* as above... */
};

#define ADV_ABORT_TIMEOUT       2000	/* time to wait for abort (mSec) */
#define ADV_WATCH_TIMEOUT       1000	/* time to wait for watchdog (mSec) */

/******************************************************************************/
/*                             Control Blocks routines                        */
/******************************************************************************/

static int
adv_alloc_ccbs(sc)
	ASC_SOFTC      *sc;
{
	bus_dma_segment_t seg;
	int             error, rseg;

	/*
         * Allocate the control blocks.
         */
	if ((error = bus_dmamem_alloc(sc->sc_dmat, sizeof(struct adv_control),
			   NBPG, 0, &seg, 1, &rseg,
			   BUS_DMA_NOWAIT | BUS_DMA_ZERO)) != 0) {
		printf("%s: unable to allocate control structures,"
		       " error = %d\n", sc->sc_dev.dv_xname, error);
		return (error);
	}
	if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg,
		   sizeof(struct adv_control), (caddr_t *) & sc->sc_control,
				 BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
		printf("%s: unable to map control structures, error = %d\n",
		       sc->sc_dev.dv_xname, error);
		return (error);
	}
	/*
         * Create and load the DMA map used for the control blocks.
         */
	if ((error = bus_dmamap_create(sc->sc_dmat, sizeof(struct adv_control),
			   1, sizeof(struct adv_control), 0, BUS_DMA_NOWAIT,
				       &sc->sc_dmamap_control)) != 0) {
		printf("%s: unable to create control DMA map, error = %d\n",
		       sc->sc_dev.dv_xname, error);
		return (error);
	}
	if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap_control,
			   sc->sc_control, sizeof(struct adv_control), NULL,
				     BUS_DMA_NOWAIT)) != 0) {
		printf("%s: unable to load control DMA map, error = %d\n",
		       sc->sc_dev.dv_xname, error);
		return (error);
	}
	return (0);
}

/*
 * Create a set of ccbs and add them to the free list.  Called once
 * by adv_init().  We return the number of CCBs successfully created.
 * CCB data is already zeroed on allocation.
 */
static int
adv_create_ccbs(sc, ccbstore, count)
	ASC_SOFTC      *sc;
	ADV_CCB        *ccbstore;
	int             count;
{
	ADV_CCB        *ccb;
	int             i, error;

	for (i = 0; i < count; i++) {
		ccb = &ccbstore[i];
		if ((error = adv_init_ccb(sc, ccb)) != 0) {
			printf("%s: unable to initialize ccb, error = %d\n",
			       sc->sc_dev.dv_xname, error);
			return (i);
		}
		TAILQ_INSERT_TAIL(&sc->sc_free_ccb, ccb, chain);
	}

	return (i);
}

/*
 * A ccb is put onto the free list.
 */
void
adv_ccb_free(xsc, xccb)
	void *xsc, *xccb;
{
	ASC_SOFTC *sc = xsc;
	ADV_CCB *ccb = xccb;

	adv_reset_ccb(ccb);

	mtx_enter(&sc->sc_ccb_mtx);
	TAILQ_INSERT_HEAD(&sc->sc_free_ccb, ccb, chain);
	mtx_leave(&sc->sc_ccb_mtx);
}

static void
adv_reset_ccb(ccb)
	ADV_CCB        *ccb;
{

	ccb->flags = 0;
}

static int
adv_init_ccb(sc, ccb)
	ASC_SOFTC      *sc;
	ADV_CCB        *ccb;
{
	int             error;

	/*
         * Create the DMA map for this CCB.
         */
	error = bus_dmamap_create(sc->sc_dmat,
				  (ASC_MAX_SG_LIST - 1) * PAGE_SIZE,
			 ASC_MAX_SG_LIST, (ASC_MAX_SG_LIST - 1) * PAGE_SIZE,
		   0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &ccb->dmamap_xfer);
	if (error) {
		printf("%s: unable to create DMA map, error = %d\n",
		       sc->sc_dev.dv_xname, error);
		return (error);
	}
	adv_reset_ccb(ccb);
	return (0);
}

/*
 * Get a free ccb
 */
void *
adv_ccb_alloc(xsc)
	void *xsc;
{
	ASC_SOFTC *sc = xsc;
	ADV_CCB *ccb;

	mtx_enter(&sc->sc_ccb_mtx);
	ccb = TAILQ_FIRST(&sc->sc_free_ccb);
	if (ccb) {
		TAILQ_REMOVE(&sc->sc_free_ccb, ccb, chain);
		ccb->flags |= CCB_ALLOC;
	}
	mtx_leave(&sc->sc_ccb_mtx);

	return (ccb);
}

/*
 * Queue a CCB to be sent to the controller, and send it if possible.
 */
static void
adv_queue_ccb(sc, ccb)
	ASC_SOFTC      *sc;
	ADV_CCB        *ccb;
{

	timeout_set(&ccb->xs->stimeout, adv_timeout, ccb);
	TAILQ_INSERT_TAIL(&sc->sc_waiting_ccb, ccb, chain);

	adv_start_ccbs(sc);
}

static void
adv_start_ccbs(sc)
	ASC_SOFTC      *sc;
{
	ADV_CCB        *ccb;
	struct scsi_xfer *xs;

	while ((ccb = TAILQ_FIRST(&sc->sc_waiting_ccb)) != NULL) {

		xs = ccb->xs;
		if (ccb->flags & CCB_WATCHDOG)
			timeout_del(&xs->stimeout);

		if (AscExeScsiQueue(sc, &ccb->scsiq) == ASC_BUSY) {
			ccb->flags |= CCB_WATCHDOG;
			timeout_set(&xs->stimeout, adv_watchdog, ccb);
			timeout_add_msec(&xs->stimeout, ADV_WATCH_TIMEOUT);
			break;
		}
		TAILQ_REMOVE(&sc->sc_waiting_ccb, ccb, chain);

		if ((ccb->xs->flags & SCSI_POLL) == 0) {
			timeout_set(&xs->stimeout, adv_timeout, ccb);
			timeout_add_msec(&xs->stimeout, ccb->timeout);
		}
	}
}

/******************************************************************************/
/*                      DMA able memory allocation routines                   */
/******************************************************************************/

/*
 * Allocate a DMA able memory for overrun_buffer.
 * This memory can be safely shared among all the AdvanSys boards.
 */
u_int8_t       *
adv_alloc_overrunbuf(dvname, dmat)
	char           *dvname;
	bus_dma_tag_t   dmat;
{
	static u_int8_t *overrunbuf = NULL;

	bus_dmamap_t    ovrbuf_dmamap;
	bus_dma_segment_t seg;
	int             rseg, error;

	/*
         * if an overrun buffer has been already allocated don't allocate it
         * again. Instead return the address of the allocated buffer.
         */
	if (overrunbuf)
		return (overrunbuf);

	if ((error = bus_dmamem_alloc(dmat, ASC_OVERRUN_BSIZE,
			   NBPG, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) {
		printf("%s: unable to allocate overrun buffer, error = %d\n",
		       dvname, error);
		return (0);
	}
	if ((error = bus_dmamem_map(dmat, &seg, rseg, ASC_OVERRUN_BSIZE,
	(caddr_t *) & overrunbuf, BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
		printf("%s: unable to map overrun buffer, error = %d\n",
		       dvname, error);

		bus_dmamem_free(dmat, &seg, 1);
		return (0);
	}
	if ((error = bus_dmamap_create(dmat, ASC_OVERRUN_BSIZE, 1,
	      ASC_OVERRUN_BSIZE, 0, BUS_DMA_NOWAIT, &ovrbuf_dmamap)) != 0) {
		printf("%s: unable to create overrun buffer DMA map,"
		       " error = %d\n", dvname, error);

		bus_dmamem_unmap(dmat, overrunbuf, ASC_OVERRUN_BSIZE);
		bus_dmamem_free(dmat, &seg, 1);
		return (0);
	}
	if ((error = bus_dmamap_load(dmat, ovrbuf_dmamap, overrunbuf,
			   ASC_OVERRUN_BSIZE, NULL, BUS_DMA_NOWAIT)) != 0) {
		printf("%s: unable to load overrun buffer DMA map,"
		       " error = %d\n", dvname, error);

		bus_dmamap_destroy(dmat, ovrbuf_dmamap);
		bus_dmamem_unmap(dmat, overrunbuf, ASC_OVERRUN_BSIZE);
		bus_dmamem_free(dmat, &seg, 1);
		return (0);
	}
	return (overrunbuf);
}

/******************************************************************************/
/*                         SCSI layer interfacing routines                    */
/******************************************************************************/

int
adv_init(sc)
	ASC_SOFTC      *sc;
{
	int             warn;

	if (!AscFindSignature(sc->sc_iot, sc->sc_ioh))
		panic("adv_init: adv_find_signature failed");

	/*
         * Read the board configuration
         */
	AscInitASC_SOFTC(sc);
	warn = AscInitFromEEP(sc);
	if (warn) {
		printf("%s -get: ", sc->sc_dev.dv_xname);
		switch (warn) {
		case -1:
			printf("Chip is not halted\n");
			break;

		case -2:
			printf("Couldn't get MicroCode Start"
			       " address\n");
			break;

		case ASC_WARN_IO_PORT_ROTATE:
			printf("I/O port address modified\n");
			break;

		case ASC_WARN_AUTO_CONFIG:
			printf("I/O port increment switch enabled\n");
			break;

		case ASC_WARN_EEPROM_CHKSUM:
			printf("EEPROM checksum error\n");
			break;

		case ASC_WARN_IRQ_MODIFIED:
			printf("IRQ modified\n");
			break;

		case ASC_WARN_CMD_QNG_CONFLICT:
			printf("tag queuing enabled w/o disconnects\n");
			break;

		default:
			printf("unknown warning %d\n", warn);
		}
	}
	if (sc->scsi_reset_wait > ASC_MAX_SCSI_RESET_WAIT)
		sc->scsi_reset_wait = ASC_MAX_SCSI_RESET_WAIT;

	/*
         * Modify the board configuration
         */
	warn = AscInitFromASC_SOFTC(sc);
	if (warn) {
		printf("%s -set: ", sc->sc_dev.dv_xname);
		switch (warn) {
		case ASC_WARN_CMD_QNG_CONFLICT:
			printf("tag queuing enabled w/o disconnects\n");
			break;

		case ASC_WARN_AUTO_CONFIG:
			printf("I/O port increment switch enabled\n");
			break;

		default:
			printf("unknown warning %d\n", warn);
		}
	}
	sc->isr_callback = (ulong) adv_narrow_isr_callback;

	if (!(sc->overrun_buf = adv_alloc_overrunbuf(sc->sc_dev.dv_xname,
						     sc->sc_dmat))) {
		return (1);
	}

	return (0);
}

void
adv_attach(sc)
	ASC_SOFTC      *sc;
{
	struct scsibus_attach_args	saa;
	int				i, error;

	/*
         * Initialize board RISC chip and enable interrupts.
         */
	switch (AscInitDriver(sc)) {
	case 0:
		/* AllOK */
		break;

	case 1:
		panic("%s: bad signature", sc->sc_dev.dv_xname);
		break;

	case 2:
		panic("%s: unable to load MicroCode",
		      sc->sc_dev.dv_xname);
		break;

	case 3:
		panic("%s: unable to initialize MicroCode",
		      sc->sc_dev.dv_xname);
		break;

	default:
		panic("%s: unable to initialize board RISC chip",
		      sc->sc_dev.dv_xname);
	}

	TAILQ_INIT(&sc->sc_free_ccb);
	TAILQ_INIT(&sc->sc_waiting_ccb);

	mtx_init(&sc->sc_ccb_mtx, IPL_BIO);
	scsi_iopool_init(&sc->sc_iopool, sc, adv_ccb_alloc, adv_ccb_free);

	/*
         * fill in the prototype scsi_link.
         */
	sc->sc_link.adapter_softc = sc;
	sc->sc_link.adapter_target = sc->chip_scsi_id;
	sc->sc_link.adapter = &adv_switch;
	sc->sc_link.openings = 4;
	sc->sc_link.pool = &sc->sc_iopool;
	sc->sc_link.adapter_buswidth = 7;

	/*
         * Allocate the Control Blocks.
         */
	error = adv_alloc_ccbs(sc);
	if (error)
		return; /* (error) */ ;

	/*
         * Create and initialize the Control Blocks.
         */
	i = adv_create_ccbs(sc, sc->sc_control->ccbs, ADV_MAX_CCB);
	if (i == 0) {
		printf("%s: unable to create control blocks\n",
		       sc->sc_dev.dv_xname);
		return; /* (ENOMEM) */ ;
	} else if (i != ADV_MAX_CCB) {
		printf("%s: WARNING: only %d of %d control blocks created\n",
		       sc->sc_dev.dv_xname, i, ADV_MAX_CCB);
	}

	bzero(&saa, sizeof(saa));
	saa.saa_sc_link = &sc->sc_link;
	config_found(&sc->sc_dev, &saa, scsiprint);
}

static void
advminphys(struct buf *bp, struct scsi_link *sl)
{
	if (bp->b_bcount > ((ASC_MAX_SG_LIST - 1) * PAGE_SIZE))
		bp->b_bcount = ((ASC_MAX_SG_LIST - 1) * PAGE_SIZE);
	minphys(bp);
}

/*
 * start a scsi operation given the command and the data address.  Also needs
 * the unit, target and lu.
 */
static void
adv_scsi_cmd(xs)
	struct scsi_xfer *xs;
{
	struct scsi_link *sc_link = xs->sc_link;
	ASC_SOFTC      *sc = sc_link->adapter_softc;
	bus_dma_tag_t   dmat = sc->sc_dmat;
	ADV_CCB        *ccb;
	int             flags, error, nsegs;

	/*
         * get a ccb to use. If the transfer
         * is from a buf (possibly from interrupt time)
         * then we can't allow it to sleep
         */

	flags = xs->flags;
	ccb = xs->io;

	ccb->xs = xs;
	ccb->timeout = xs->timeout;

	/*
         * Build up the request
         */
	memset(&ccb->scsiq, 0, sizeof(ASC_SCSI_Q));

	ccb->scsiq.q2.ccb_ptr = (ulong) ccb;

	ccb->scsiq.cdbptr = &xs->cmd->opcode;
	ccb->scsiq.q2.cdb_len = xs->cmdlen;
	ccb->scsiq.q1.target_id = ASC_TID_TO_TARGET_ID(sc_link->target);
	ccb->scsiq.q1.target_lun = sc_link->lun;
	ccb->scsiq.q2.target_ix = ASC_TIDLUN_TO_IX(sc_link->target,
						   sc_link->lun);
	ccb->scsiq.q1.sense_addr = sc->sc_dmamap_control->dm_segs[0].ds_addr +
		ADV_CCB_OFF(ccb) + offsetof(struct adv_ccb, scsi_sense);
	ccb->scsiq.q1.sense_len = sizeof(struct scsi_sense_data);

	/*
         * If  there  are  any  outstanding  requests  for  the  current target,
         * then  every  255th request  send an  ORDERED request.  This heuristic
         * tries  to  retain  the  benefit  of request  sorting while preventing
         * request starvation. 255 is the max number of tags or pending commands
         * a device may have outstanding.
         */
	sc->reqcnt[sc_link->target]++;
	if ((sc->reqcnt[sc_link->target] > 0) &&
	    (sc->reqcnt[sc_link->target] % 255) == 0) {
		ccb->scsiq.q2.tag_code = M2_QTAG_MSG_ORDERED;
	} else {
		ccb->scsiq.q2.tag_code = M2_QTAG_MSG_SIMPLE;
	}

	if (xs->datalen) {
		/*
                 * Map the DMA transfer.
                 */
		error = bus_dmamap_load(dmat,
		      ccb->dmamap_xfer, xs->data, xs->datalen, NULL,
					(flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK);

		if (error) {
			if (error == EFBIG) {
				printf("%s: adv_scsi_cmd, more than %d dma"
				       " segments\n",
				       sc->sc_dev.dv_xname, ASC_MAX_SG_LIST);
			} else {
				printf("%s: adv_scsi_cmd, error %d loading"
				       " dma map\n",
				       sc->sc_dev.dv_xname, error);
			}

			xs->error = XS_DRIVER_STUFFUP;
			scsi_done(xs);
			return;
		}
		bus_dmamap_sync(dmat, ccb->dmamap_xfer,
		    0, ccb->dmamap_xfer->dm_mapsize,
		    ((flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD :
			BUS_DMASYNC_PREWRITE));

		memset(&ccb->sghead, 0, sizeof(ASC_SG_HEAD));

		for (nsegs = 0; nsegs < ccb->dmamap_xfer->dm_nsegs; nsegs++) {

			ccb->sghead.sg_list[nsegs].addr =
				ccb->dmamap_xfer->dm_segs[nsegs].ds_addr;
			ccb->sghead.sg_list[nsegs].bytes =
				ccb->dmamap_xfer->dm_segs[nsegs].ds_len;
		}

		ccb->sghead.entry_cnt = ccb->scsiq.q1.sg_queue_cnt =
			ccb->dmamap_xfer->dm_nsegs;

		ccb->scsiq.q1.cntl |= ASC_QC_SG_HEAD;
		ccb->scsiq.sg_head = &ccb->sghead;
		ccb->scsiq.q1.data_addr = 0;
		ccb->scsiq.q1.data_cnt = 0;
	} else {
		/*
                 * No data xfer, use non S/G values.
                 */
		ccb->scsiq.q1.data_addr = 0;
		ccb->scsiq.q1.data_cnt = 0;
	}

#ifdef ASC_DEBUG
	printf("id = %d, lun = %d, cmd = %d, ccb = 0x%lX \n",
			sc_link->scsipi_scsi.target,
			sc_link->scsipi_scsi.lun, xs->cmd->opcode,
			(unsigned long)ccb);
#endif
	/*
         * Usually return SUCCESSFULLY QUEUED
         */
	if ((flags & SCSI_POLL) == 0)
		return;

	/*
         * If we can't use interrupts, poll on completion
         */
	if (adv_poll(sc, xs, ccb->timeout)) {
		adv_timeout(ccb);
		if (adv_poll(sc, xs, ccb->timeout))
			adv_timeout(ccb);
	}
}

int
adv_intr(arg)
	void           *arg;
{
	ASC_SOFTC      *sc = arg;

#ifdef ASC_DEBUG
	int int_pend = FALSE;

	if(ASC_IS_INT_PENDING(sc->sc_iot, sc->sc_ioh))
	{
		int_pend = TRUE;
		printf("ISR - ");
	}
#endif
	AscISR(sc);
#ifdef ASC_DEBUG
	if(int_pend)
		printf("\n");
#endif

	return (1);
}

/*
 * Poll a particular unit, looking for a particular xs
 */
static int
adv_poll(sc, xs, count)
	ASC_SOFTC      *sc;
	struct scsi_xfer *xs;
	int             count;
{
	int s;

	/* timeouts are in msec, so we loop in 1000 usec cycles */
	while (count) {
		s = splbio();
		adv_intr(sc);
		splx(s);
		if (xs->flags & ITSDONE)
			return (0);
		delay(1000);	/* only happens in boot so ok */
		count--;
	}
	return (1);
}

static void
adv_timeout(arg)
	void           *arg;
{
	ADV_CCB        *ccb = arg;
	struct scsi_xfer *xs = ccb->xs;
	struct scsi_link *sc_link = xs->sc_link;
	ASC_SOFTC      *sc = sc_link->adapter_softc;
	int             s;

	sc_print_addr(sc_link);
	printf("timed out");

	s = splbio();

	/*
         * If it has been through before, then a previous abort has failed,
         * don't try abort again, reset the bus instead.
         */
	if (ccb->flags & CCB_ABORT) {
		/* abort timed out */
		printf(" AGAIN. Resetting Bus\n");
		/* Lets try resetting the bus! */
		if (AscResetBus(sc) == ASC_ERROR) {
			ccb->timeout = sc->scsi_reset_wait;
			adv_queue_ccb(sc, ccb);
		}
	} else {
		/* abort the operation that has timed out */
		printf("\n");
		AscAbortCCB(sc, (u_int32_t) ccb);
		ccb->xs->error = XS_TIMEOUT;
		ccb->timeout = ADV_ABORT_TIMEOUT;
		ccb->flags |= CCB_ABORT;
		adv_queue_ccb(sc, ccb);
	}

	splx(s);
}

static void
adv_watchdog(arg)
	void           *arg;
{
	ADV_CCB        *ccb = arg;
	struct scsi_xfer *xs = ccb->xs;
	struct scsi_link *sc_link = xs->sc_link;
	ASC_SOFTC      *sc = sc_link->adapter_softc;
	int             s;

	s = splbio();

	ccb->flags &= ~CCB_WATCHDOG;
	adv_start_ccbs(sc);

	splx(s);
}

/******************************************************************************/
/*                  NARROW and WIDE boards Interrupt callbacks                */
/******************************************************************************/

/*
 * adv_narrow_isr_callback() - Second Level Interrupt Handler called by AscISR()
 *
 * Interrupt callback function for the Narrow SCSI Asc Library.
 */
static void
adv_narrow_isr_callback(sc, qdonep)
	ASC_SOFTC      *sc;
	ASC_QDONE_INFO *qdonep;
{
	bus_dma_tag_t   dmat = sc->sc_dmat;
	ADV_CCB        *ccb = (ADV_CCB *) qdonep->d2.ccb_ptr;
	struct scsi_xfer *xs = ccb->xs;
	struct scsi_sense_data *s1, *s2;

#ifdef ASC_DEBUG
	printf(" - ccb=0x%lx, id=%d, lun=%d, cmd=%d, ",
			(unsigned long)ccb,
			xs->sc_link->scsipi_scsi.target,
			xs->sc_link->scsipi_scsi.lun, xs->cmd->opcode);
#endif
	timeout_del(&xs->stimeout);

	/*
         * If we were a data transfer, unload the map that described
         * the data buffer.
         */
	if (xs->datalen) {
		bus_dmamap_sync(dmat, ccb->dmamap_xfer,
		    0, ccb->dmamap_xfer->dm_mapsize,
		    ((xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD :
			BUS_DMASYNC_POSTWRITE));
		bus_dmamap_unload(dmat, ccb->dmamap_xfer);
	}
	if ((ccb->flags & CCB_ALLOC) == 0) {
		panic("%s: exiting ccb not allocated!", sc->sc_dev.dv_xname);
		return;
	}
	/*
         * 'qdonep' contains the command's ending status.
         */
#ifdef ASC_DEBUG
	printf("d_s=%d, h_s=%d", qdonep->d3.done_stat, qdonep->d3.host_stat);
#endif
	switch (qdonep->d3.done_stat) {
	case ASC_QD_NO_ERROR:
		switch (qdonep->d3.host_stat) {
		case ASC_QHSTA_NO_ERROR:
			xs->error = XS_NOERROR;
			xs->resid = 0;
			break;

		default:
			/* QHSTA error occurred */
			xs->error = XS_DRIVER_STUFFUP;
			break;
		}

		/*
                 * If an INQUIRY command completed successfully, then call
                 * the AscInquiryHandling() function to patch bugged boards.
                 */
		if ((xs->cmd->opcode == SCSICMD_Inquiry) &&
		    (xs->sc_link->lun == 0) &&
		    (xs->datalen - qdonep->remain_bytes) >= 8) {
			AscInquiryHandling(sc,
				      xs->sc_link->target & 0x7,
					   (ASC_SCSI_INQUIRY *) xs->data);
		}
		break;

	case ASC_QD_WITH_ERROR:
		switch (qdonep->d3.host_stat) {
		case ASC_QHSTA_NO_ERROR:
			if (qdonep->d3.scsi_stat == SS_CHK_CONDITION) {
				s1 = &ccb->scsi_sense;
				s2 = &xs->sense;
				*s2 = *s1;
				xs->error = XS_SENSE;
			} else {
				xs->error = XS_DRIVER_STUFFUP;
			}
			break;

		default:
			/* QHSTA error occurred */
			xs->error = XS_DRIVER_STUFFUP;
			break;
		}
		break;

	case ASC_QD_ABORTED_BY_HOST:
	default:
		xs->error = XS_DRIVER_STUFFUP;
		break;
	}

	scsi_done(xs);
}


code:
/*	$OpenBSD: adv.h,v 1.6 2008/06/26 05:42:15 ray Exp $	*/
/*      $NetBSD: adv.h,v 1.3 1998/09/26 16:02:56 dante Exp $        */

/*
 * Generic driver definitions and exported functions for the Advanced
 * Systems Inc. Narrow SCSI controllers
 *
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * Author: Baldassare Dante Profeta <dante@mclink.it>
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS
 * 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.
 */

#ifndef _ADVANSYS_NARROW_H_
#define _ADVANSYS_NARROW_H_

#include <dev/ic/advlib.h>

/******************************************************************************/

struct adv_ccb
{
	ASC_SG_HEAD	sghead;
	ASC_SCSI_Q	scsiq;

	struct scsi_sense_data scsi_sense;

	TAILQ_ENTRY(adv_ccb) chain;
	struct scsi_xfer	*xs;	/* the scsi_xfer for this cmd */
	int			flags;	/* see below */

	int			timeout;
	/*
	 * This DMA map maps the buffer involved in the transfer.
	 */
	bus_dmamap_t		dmamap_xfer;
};

typedef struct adv_ccb ADV_CCB;

/* flags for ADV_CCB */
#define CCB_ALLOC       0x01
#define CCB_ABORT       0x02
#define	CCB_WATCHDOG	0x10

#define ADV_MAX_CCB	32

struct adv_control
{
	ADV_CCB	ccbs[ADV_MAX_CCB];	/* all our control blocks */
};

/*
 * Offset of a CCB from the beginning of the control DMA mapping.
 */
#define	ADV_CCB_OFF(c)	(offsetof(struct adv_control, ccbs[0]) +	\
		    (((u_long)(c)) - ((u_long)&sc->sc_control->ccbs[0])))

/******************************************************************************/

int adv_init(ASC_SOFTC *sc);
void adv_attach(ASC_SOFTC *sc);
int adv_intr(void *arg);

/******************************************************************************/

#endif /* _ADVANSYS_NARROW_H_ */